Various fixes
* Fix parsing soft-keywords as naked identifiers * Create proper correlations during parsing * Fix some type inference issues with entity valued paths
This commit is contained in:
parent
075cc8d108
commit
335ed19821
|
@ -191,15 +191,14 @@ entityName
|
||||||
*/
|
*/
|
||||||
variable
|
variable
|
||||||
: AS identifier
|
: AS identifier
|
||||||
| IDENTIFIER
|
| nakedIdentifier
|
||||||
| QUOTED_IDENTIFIER
|
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 'cross join' to a second root entity (a cartesian product)
|
* A 'cross join' to a second root entity (a cartesian product)
|
||||||
*/
|
*/
|
||||||
crossJoin
|
crossJoin
|
||||||
: CROSS JOIN rootEntity variable?
|
: CROSS JOIN entityName variable?
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1461,7 +1460,8 @@ rollup
|
||||||
* This parser rule helps with that. Here we expect that the caller already understands their
|
* This parser rule helps with that. Here we expect that the caller already understands their
|
||||||
* context enough to know that keywords-as-identifiers are allowed.
|
* context enough to know that keywords-as-identifiers are allowed.
|
||||||
*/
|
*/
|
||||||
identifier
|
// All except the possible optional following keywords LEFT, RIGHT, INNER, FULL, OUTER
|
||||||
|
nakedIdentifier
|
||||||
: IDENTIFIER
|
: IDENTIFIER
|
||||||
| QUOTED_IDENTIFIER
|
| QUOTED_IDENTIFIER
|
||||||
| (ALL
|
| (ALL
|
||||||
|
@ -1510,7 +1510,7 @@ identifier
|
||||||
| FOR
|
| FOR
|
||||||
| FORMAT
|
| FORMAT
|
||||||
| FROM
|
| FROM
|
||||||
| FULL
|
// | FULL
|
||||||
| FUNCTION
|
| FUNCTION
|
||||||
| GROUP
|
| GROUP
|
||||||
| GROUPS
|
| GROUPS
|
||||||
|
@ -1522,7 +1522,7 @@ identifier
|
||||||
| IN
|
| IN
|
||||||
| INDEX
|
| INDEX
|
||||||
| INDICES
|
| INDICES
|
||||||
| INNER
|
// | INNER
|
||||||
| INSERT
|
| INSERT
|
||||||
| INSTANT
|
| INSTANT
|
||||||
| INTERSECT
|
| INTERSECT
|
||||||
|
@ -1532,7 +1532,7 @@ identifier
|
||||||
| KEY
|
| KEY
|
||||||
| LAST
|
| LAST
|
||||||
| LEADING
|
| LEADING
|
||||||
| LEFT
|
// | LEFT
|
||||||
| LIKE
|
| LIKE
|
||||||
| LIMIT
|
| LIMIT
|
||||||
| LIST
|
| LIST
|
||||||
|
@ -1569,7 +1569,7 @@ identifier
|
||||||
| OR
|
| OR
|
||||||
| ORDER
|
| ORDER
|
||||||
| OTHERS
|
| OTHERS
|
||||||
| OUTER
|
// | OUTER
|
||||||
| OVER
|
| OVER
|
||||||
| OVERFLOW
|
| OVERFLOW
|
||||||
| OVERLAY
|
| OVERLAY
|
||||||
|
@ -1582,7 +1582,7 @@ identifier
|
||||||
| QUARTER
|
| QUARTER
|
||||||
| RANGE
|
| RANGE
|
||||||
| RESPECT
|
| RESPECT
|
||||||
| RIGHT
|
// | RIGHT
|
||||||
| ROLLUP
|
| ROLLUP
|
||||||
| ROW
|
| ROW
|
||||||
| ROWS
|
| ROWS
|
||||||
|
@ -1621,3 +1621,13 @@ identifier
|
||||||
logUseOfReservedWordAsIdentifier( getCurrentToken() );
|
logUseOfReservedWordAsIdentifier( getCurrentToken() );
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
identifier
|
||||||
|
: nakedIdentifier
|
||||||
|
| (FULL
|
||||||
|
| INNER
|
||||||
|
| LEFT
|
||||||
|
| OUTER
|
||||||
|
| RIGHT) {
|
||||||
|
logUseOfReservedWordAsIdentifier( getCurrentToken() );
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
|
@ -548,7 +548,9 @@ public class EntityCollectionPart
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForeignKeyDescriptor.Nature getSideNature() {
|
public ForeignKeyDescriptor.Nature getSideNature() {
|
||||||
return ForeignKeyDescriptor.Nature.TARGET;
|
return collectionDescriptor.isOneToMany()
|
||||||
|
? ForeignKeyDescriptor.Nature.TARGET
|
||||||
|
: ForeignKeyDescriptor.Nature.KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
.getCurrent()
|
.getCurrent()
|
||||||
.getPathRegistry();
|
.getPathRegistry();
|
||||||
|
|
||||||
final SqmFrom<?,?> pathRootByAlias = sqmPathRegistry.findFromByAlias( identifier );
|
final SqmFrom<?,?> pathRootByAlias = sqmPathRegistry.findFromByAlias( identifier, true );
|
||||||
if ( pathRootByAlias != null ) {
|
if ( pathRootByAlias != null ) {
|
||||||
// identifier is an alias (identification variable)
|
// identifier is an alias (identification variable)
|
||||||
validateAsRoot( pathRootByAlias );
|
validateAsRoot( pathRootByAlias );
|
||||||
|
|
|
@ -110,7 +110,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||||
|
|
||||||
final SqmFrom<?, Object> pathRootByAlias = pathRegistry.findFromByAlias( identifier );
|
final SqmFrom<?, Object> pathRootByAlias = pathRegistry.findFromByAlias( identifier, true );
|
||||||
if ( pathRootByAlias != null ) {
|
if ( pathRootByAlias != null ) {
|
||||||
// identifier is an alias (identification variable)
|
// identifier is an alias (identification variable)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.sqm.tree.SqmQuery;
|
import org.hibernate.query.sqm.tree.SqmQuery;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
||||||
|
@ -45,7 +46,7 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum
|
||||||
final SqmRoot<?> root = pathRoot.findRoot();
|
final SqmRoot<?> root = pathRoot.findRoot();
|
||||||
final SqmRoot<?> joinRoot = sqmJoin.findRoot();
|
final SqmRoot<?> joinRoot = sqmJoin.findRoot();
|
||||||
if ( root != joinRoot ) {
|
if ( root != joinRoot ) {
|
||||||
// The root of a path within a query doesn't have the same root as the current join we are processing.
|
// The root of a path within a join condition doesn't have the same root as the current join we are processing.
|
||||||
// The aim of this check is to prevent uses of different "spaces" i.e. `from A a, B b join b.id = a.id` would be illegal
|
// The aim of this check is to prevent uses of different "spaces" i.e. `from A a, B b join b.id = a.id` would be illegal
|
||||||
SqmCreationProcessingState processingState = getCreationState().getCurrentProcessingState();
|
SqmCreationProcessingState processingState = getCreationState().getCurrentProcessingState();
|
||||||
// First, we need to find out if the current join is part of current processing query
|
// First, we need to find out if the current join is part of current processing query
|
||||||
|
@ -56,7 +57,14 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum
|
||||||
// If the current processing query contains the root of the current join,
|
// If the current processing query contains the root of the current join,
|
||||||
// then the root of the processing path must be a root of one of the parent queries
|
// then the root of the processing path must be a root of one of the parent queries
|
||||||
if ( fromClause != null && fromClause.getRoots().contains( joinRoot ) ) {
|
if ( fromClause != null && fromClause.getRoots().contains( joinRoot ) ) {
|
||||||
validateAsRootOnParentQueryClosure( pathRoot, root, processingState.getParentProcessingState() );
|
// It is allowed to use correlations from the same query
|
||||||
|
if ( !( root instanceof SqmCorrelation<?, ?> ) || !fromClause.getRoots().contains( root ) ) {
|
||||||
|
validateAsRootOnParentQueryClosure(
|
||||||
|
pathRoot,
|
||||||
|
root,
|
||||||
|
processingState.getParentProcessingState()
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,6 @@ import org.hibernate.query.sqm.UnaryArithmeticOperator;
|
||||||
import org.hibernate.query.sqm.UnknownEntityException;
|
import org.hibernate.query.sqm.UnknownEntityException;
|
||||||
import org.hibernate.query.sqm.function.FunctionKind;
|
import org.hibernate.query.sqm.function.FunctionKind;
|
||||||
import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor;
|
import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
|
|
||||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.internal.ParameterCollector;
|
import org.hibernate.query.sqm.internal.ParameterCollector;
|
||||||
import org.hibernate.query.sqm.internal.SqmCreationProcessingStateImpl;
|
import org.hibernate.query.sqm.internal.SqmCreationProcessingStateImpl;
|
||||||
|
@ -1047,7 +1046,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
@Override
|
@Override
|
||||||
public SqmPath<?> visitJpaSelectObjectSyntax(HqlParser.JpaSelectObjectSyntaxContext ctx) {
|
public SqmPath<?> visitJpaSelectObjectSyntax(HqlParser.JpaSelectObjectSyntaxContext ctx) {
|
||||||
final String alias = ctx.getChild( 2 ).getText();
|
final String alias = ctx.getChild( 2 ).getText();
|
||||||
final SqmFrom<?, ?> sqmFromByAlias = processingStateStack.getCurrent().getPathRegistry().findFromByAlias( alias );
|
final SqmFrom<?, ?> sqmFromByAlias = processingStateStack.getCurrent().getPathRegistry().findFromByAlias(
|
||||||
|
alias,
|
||||||
|
true
|
||||||
|
);
|
||||||
if ( sqmFromByAlias == null ) {
|
if ( sqmFromByAlias == null ) {
|
||||||
throw new SemanticException( "Unable to resolve alias [" + alias + "] in selection [" + ctx.getText() + "]" );
|
throw new SemanticException( "Unable to resolve alias [" + alias + "] in selection [" + ctx.getText() + "]" );
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1104,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return new SqmAliasedNodeRef( correspondingPosition, integerDomainType, creationContext.getNodeBuilder() );
|
return new SqmAliasedNodeRef( correspondingPosition, integerDomainType, creationContext.getNodeBuilder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias( identifierText );
|
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias(
|
||||||
|
identifierText,
|
||||||
|
true
|
||||||
|
);
|
||||||
if ( sqmFrom != null ) {
|
if ( sqmFrom != null ) {
|
||||||
if ( definedCollate ) {
|
if ( definedCollate ) {
|
||||||
// This is syntactically disallowed
|
// This is syntactically disallowed
|
||||||
|
@ -1360,6 +1365,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitIdentifier(HqlParser.IdentifierContext ctx) {
|
public String visitIdentifier(HqlParser.IdentifierContext ctx) {
|
||||||
|
final ParseTree child = ctx.getChild( 0 );
|
||||||
|
if ( child instanceof TerminalNode ) {
|
||||||
|
return child.getText();
|
||||||
|
}
|
||||||
|
return visitNakedIdentifier( (HqlParser.NakedIdentifierContext) child );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitNakedIdentifier(HqlParser.NakedIdentifierContext ctx) {
|
||||||
final TerminalNode node = (TerminalNode) ctx.getChild( 0 );
|
final TerminalNode node = (TerminalNode) ctx.getChild( 0 );
|
||||||
if ( node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER ) {
|
if ( node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER ) {
|
||||||
return QuotingHelper.unquoteIdentifier( node.getText() );
|
return QuotingHelper.unquoteIdentifier( node.getText() );
|
||||||
|
@ -1413,7 +1427,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
@Override
|
@Override
|
||||||
public SqmRoot<?> visitEntityWithJoins(HqlParser.EntityWithJoinsContext parserSpace) {
|
public SqmRoot<?> visitEntityWithJoins(HqlParser.EntityWithJoinsContext parserSpace) {
|
||||||
final SqmRoot<?> sqmRoot = visitRootEntity( (HqlParser.RootEntityContext) parserSpace.getChild( 0 ) );
|
final SqmRoot<?> sqmRoot = visitRootEntity( (HqlParser.RootEntityContext) parserSpace.getChild( 0 ) );
|
||||||
currentQuerySpec().getFromClause().addRoot( sqmRoot );
|
final SqmFromClause fromClause = currentQuerySpec().getFromClause();
|
||||||
|
// Correlations are implicitly added to the from clause
|
||||||
|
if ( !( sqmRoot instanceof SqmCorrelation<?, ?> ) ) {
|
||||||
|
fromClause.addRoot( sqmRoot );
|
||||||
|
}
|
||||||
final int size = parserSpace.getChildCount();
|
final int size = parserSpace.getChildCount();
|
||||||
for ( int i = 1; i < size; i++ ) {
|
for ( int i = 1; i < size; i++ ) {
|
||||||
final ParseTree parseTree = parserSpace.getChild( i );
|
final ParseTree parseTree = parserSpace.getChild( i );
|
||||||
|
@ -1461,12 +1479,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
// Handle the use of a correlation path in subqueries
|
// Handle the use of a correlation path in subqueries
|
||||||
if ( processingStateStack.depth() > 1 && size > 2 ) {
|
if ( processingStateStack.depth() > 1 && size > 2 ) {
|
||||||
final String parentAlias = entityNameParseTreeChildren.get( 0 ).getText();
|
final String parentAlias = entityNameParseTreeChildren.get( 0 ).getText();
|
||||||
final AbstractSqmFrom<?, ?> correlationBasis = processingState.getParentProcessingState()
|
final AbstractSqmFrom<?, ?> correlation = processingState.getPathRegistry()
|
||||||
.getPathRegistry()
|
.findFromByAlias( parentAlias, true );
|
||||||
.findFromByAlias( parentAlias );
|
if ( correlation instanceof SqmCorrelation<?, ?> ) {
|
||||||
if ( correlationBasis != null ) {
|
|
||||||
final SqmCorrelation<?, ?> correlation = correlationBasis.createCorrelation();
|
|
||||||
pathRegistry.register( correlation );
|
|
||||||
final DotIdentifierConsumer dotIdentifierConsumer = new QualifiedJoinPathConsumer(
|
final DotIdentifierConsumer dotIdentifierConsumer = new QualifiedJoinPathConsumer(
|
||||||
correlation,
|
correlation,
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
|
@ -1487,7 +1502,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
false,
|
false,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
return correlation.getCorrelatedRoot();
|
return ( (SqmCorrelation<?, ?>) correlation ).getCorrelatedRoot();
|
||||||
}
|
}
|
||||||
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'" );
|
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'" );
|
||||||
}
|
}
|
||||||
|
@ -1545,11 +1560,24 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return visitIdentifier( identifierContext );
|
return visitIdentifier( identifierContext );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final TerminalNode node = (TerminalNode) lastChild;
|
final HqlParser.NakedIdentifierContext identifierContext = (HqlParser.NakedIdentifierContext) lastChild;
|
||||||
if ( node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER ) {
|
// in this branch, the alias could be a reserved word ("keyword as identifier")
|
||||||
return QuotingHelper.unquoteIdentifier( node.getText() );
|
// which JPA disallows...
|
||||||
|
if ( getCreationOptions().useStrictJpaCompliance() ) {
|
||||||
|
final Token identificationVariableToken = identifierContext.getStart();
|
||||||
|
if ( identificationVariableToken.getType() != IDENTIFIER ) {
|
||||||
|
throw new StrictJpaComplianceViolation(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"Strict JPQL compliance was violated : %s [%s]",
|
||||||
|
StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS.description(),
|
||||||
|
identificationVariableToken.getText()
|
||||||
|
),
|
||||||
|
StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return node.getText();
|
return visitNakedIdentifier( identifierContext );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1571,8 +1599,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot<T> sqmRoot) {
|
private <T> void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot<T> sqmRoot) {
|
||||||
final HqlParser.RootEntityContext pathRootContext = (HqlParser.RootEntityContext) parserJoin.getChild( 2 );
|
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) parserJoin.getChild( 2 );
|
||||||
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) pathRootContext.getChild( 0 );
|
|
||||||
final String name = getEntityName( entityNameContext );
|
final String name = getEntityName( entityNameContext );
|
||||||
|
|
||||||
SqmTreeCreationLogger.LOGGER.debugf( "Handling root path - %s", name );
|
SqmTreeCreationLogger.LOGGER.debugf( "Handling root path - %s", name );
|
||||||
|
@ -1584,8 +1611,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
throw new SemanticException( "Unmapped polymorphic reference cannot be used as a CROSS JOIN target" );
|
throw new SemanticException( "Unmapped polymorphic reference cannot be used as a CROSS JOIN target" );
|
||||||
}
|
}
|
||||||
final HqlParser.VariableContext identificationVariableDefContext;
|
final HqlParser.VariableContext identificationVariableDefContext;
|
||||||
if ( pathRootContext.getChildCount() > 1 ) {
|
if ( parserJoin.getChildCount() > 3 ) {
|
||||||
identificationVariableDefContext = (HqlParser.VariableContext) pathRootContext.getChild( 1 );
|
identificationVariableDefContext = (HqlParser.VariableContext) parserJoin.getChild( 3 );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
identificationVariableDefContext = null;
|
identificationVariableDefContext = null;
|
||||||
|
@ -3628,6 +3655,16 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
else {
|
else {
|
||||||
//for the shorter legacy Hibernate syntax "field(arg)"
|
//for the shorter legacy Hibernate syntax "field(arg)"
|
||||||
extractFieldExpression = (SqmExtractUnit<?>) ctx.getChild( 0 ).accept(this);
|
extractFieldExpression = (SqmExtractUnit<?>) ctx.getChild( 0 ).accept(this);
|
||||||
|
// //Prefer an existing native version if available
|
||||||
|
// final SqmFunctionDescriptor functionDescriptor = getFunctionDescriptor( extractFieldExpression.getUnit().name() );
|
||||||
|
// if ( functionDescriptor != null ) {
|
||||||
|
// return functionDescriptor.generateSqmExpression(
|
||||||
|
// expressionToExtract,
|
||||||
|
// null,
|
||||||
|
// creationContext.getQueryEngine(),
|
||||||
|
// creationContext.getJpaMetamodel().getTypeConfiguration()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return getFunctionDescriptor("extract").generateSqmExpression(
|
return getFunctionDescriptor("extract").generateSqmExpression(
|
||||||
|
|
|
@ -14,17 +14,23 @@ import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.hibernate.jpa.spi.JpaCompliance;
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.query.spi.NavigablePath;
|
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||||
|
import org.hibernate.query.spi.NavigablePath;
|
||||||
import org.hibernate.query.sqm.AliasCollisionException;
|
import org.hibernate.query.sqm.AliasCollisionException;
|
||||||
import org.hibernate.query.sqm.ParsingException;
|
import org.hibernate.query.sqm.ParsingException;
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
import org.hibernate.query.sqm.SqmTreeCreationLogger;
|
import org.hibernate.query.sqm.SqmTreeCreationLogger;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
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.SqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
|
import org.hibernate.query.sqm.tree.select.SqmAliasedNode;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Join;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for indexing needed while building an SQM tree.
|
* Container for indexing needed while building an SQM tree.
|
||||||
|
@ -117,27 +123,12 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X extends SqmFrom<?, ?>> X findFromByPath(NavigablePath navigablePath) {
|
public <X extends SqmFrom<?, ?>> X findFromByPath(NavigablePath navigablePath) {
|
||||||
final SqmFrom<?, ?> found = sqmFromByPath.get( navigablePath );
|
//noinspection unchecked
|
||||||
if ( found != null ) {
|
return (X) sqmFromByPath.get( navigablePath );
|
||||||
//noinspection unchecked
|
|
||||||
return (X) found;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( associatedProcessingState.getParentProcessingState() != null ) {
|
|
||||||
final X containingQueryFrom = associatedProcessingState.getParentProcessingState()
|
|
||||||
.getPathRegistry()
|
|
||||||
.findFromByPath( navigablePath );
|
|
||||||
if ( containingQueryFrom != null ) {
|
|
||||||
// todo (6.0) create a correlation?
|
|
||||||
return containingQueryFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X extends SqmFrom<?, ?>> X findFromByAlias(String alias) {
|
public <X extends SqmFrom<?, ?>> X findFromByAlias(String alias, boolean searchParent) {
|
||||||
final String localAlias = jpaCompliance.isJpaQueryComplianceEnabled()
|
final String localAlias = jpaCompliance.isJpaQueryComplianceEnabled()
|
||||||
? alias.toLowerCase( Locale.getDefault() )
|
? alias.toLowerCase( Locale.getDefault() )
|
||||||
: alias;
|
: alias;
|
||||||
|
@ -149,8 +140,39 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
||||||
return (X) registered;
|
return (X) registered;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( associatedProcessingState.getParentProcessingState() != null ) {
|
SqmCreationProcessingState parentProcessingState = associatedProcessingState.getParentProcessingState();
|
||||||
return associatedProcessingState.getParentProcessingState().getPathRegistry().findFromByAlias( alias );
|
if ( searchParent && parentProcessingState != null ) {
|
||||||
|
X parentRegistered;
|
||||||
|
do {
|
||||||
|
parentRegistered = parentProcessingState.getPathRegistry().findFromByAlias(
|
||||||
|
alias,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
parentProcessingState = parentProcessingState.getParentProcessingState();
|
||||||
|
} while (parentProcessingState != null && parentRegistered == null);
|
||||||
|
if ( parentRegistered != null ) {
|
||||||
|
// If a parent query contains the alias, we need to create a correlation on the subquery
|
||||||
|
final SqmSubQuery<?> selectQuery = ( SqmSubQuery<?> ) associatedProcessingState.getProcessingQuery();
|
||||||
|
SqmFrom<?, ?> correlated;
|
||||||
|
if ( parentRegistered instanceof Root<?> ) {
|
||||||
|
correlated = selectQuery.correlate( (Root<?>) parentRegistered );
|
||||||
|
}
|
||||||
|
else if ( parentRegistered instanceof Join<?, ?> ) {
|
||||||
|
correlated = selectQuery.correlate( (Join<?, ?>) parentRegistered );
|
||||||
|
}
|
||||||
|
else if ( parentRegistered instanceof SqmCrossJoin<?> ) {
|
||||||
|
correlated = selectQuery.correlate( (SqmCrossJoin<?>) parentRegistered );
|
||||||
|
}
|
||||||
|
else if ( parentRegistered instanceof SqmEntityJoin<?> ) {
|
||||||
|
correlated = selectQuery.correlate( (SqmEntityJoin<?>) parentRegistered );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new UnsupportedOperationException( "Can't correlate from node: " + parentRegistered );
|
||||||
|
}
|
||||||
|
register( correlated );
|
||||||
|
//noinspection unchecked
|
||||||
|
return (X) correlated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -33,12 +33,12 @@ public interface SqmPathRegistry {
|
||||||
void register(SqmPath<?> sqmPath);
|
void register(SqmPath<?> sqmPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a SqmFrom by its identification variable (alias). Will search any
|
* Find a SqmFrom by its identification variable (alias).
|
||||||
* parent contexts as well
|
* If the SqmFrom is found in a parent context, the correlation for the path will be returned.
|
||||||
*
|
*
|
||||||
* @return matching SqmFrom or {@code null}
|
* @return matching SqmFrom or {@code null}
|
||||||
*/
|
*/
|
||||||
<X extends SqmFrom<?, ?>> X findFromByAlias(String identificationVariable);
|
<X extends SqmFrom<?, ?>> X findFromByAlias(String identificationVariable, boolean searchParent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a SqmFrom by its NavigablePath. Will search any parent contexts as well
|
* Find a SqmFrom by its NavigablePath. Will search any parent contexts as well
|
||||||
|
|
|
@ -43,8 +43,8 @@ public class FromClauseAccessImpl implements FromClauseAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup findTableGroupOnLeaf(NavigablePath navigablePath) {
|
public TableGroup findTableGroupOnParents(NavigablePath navigablePath) {
|
||||||
return findTableGroup( navigablePath );
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
package org.hibernate.query.sqm.internal;
|
package org.hibernate.query.sqm.internal;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import jakarta.persistence.metamodel.Bindable;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.MappingMetamodel;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||||
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
|
@ -25,7 +26,6 @@ import org.hibernate.metamodel.model.domain.internal.AnyMappingSqmPathSource;
|
||||||
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
|
||||||
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
|
||||||
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
||||||
import org.hibernate.metamodel.MappingMetamodel;
|
|
||||||
import org.hibernate.metamodel.model.domain.internal.MappedSuperclassSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.MappedSuperclassSqmPathSource;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.spi.NavigablePath;
|
import org.hibernate.query.spi.NavigablePath;
|
||||||
|
@ -39,6 +39,8 @@ import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
|
||||||
|
import jakarta.persistence.metamodel.Bindable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for dealing with Hibernate's "mapping model" while processing an SQM which is defined
|
* Helper for dealing with Hibernate's "mapping model" while processing an SQM which is defined
|
||||||
* in terms of the JPA/SQM metamodel
|
* in terms of the JPA/SQM metamodel
|
||||||
|
@ -165,7 +167,17 @@ public class SqmMappingModelHelper {
|
||||||
sqmPath.getLhs().getReferencedPathSource().getPathName(),
|
sqmPath.getLhs().getReferencedPathSource().getPathName(),
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
return pluralPart.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
final CollectionPart collectionPart = (CollectionPart) pluralPart.findSubPart(
|
||||||
|
sqmPath.getReferencedPathSource()
|
||||||
|
.getPathName(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
// For entity collection parts, we must return the entity mapping type,
|
||||||
|
// as that is the mapping type of the expression
|
||||||
|
if ( collectionPart instanceof EntityCollectionPart ) {
|
||||||
|
return ( (EntityCollectionPart) collectionPart ).getEntityMappingType();
|
||||||
|
}
|
||||||
|
return collectionPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sqmPath.getLhs() == null ) {
|
if ( sqmPath.getLhs() == null ) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.metamodel.MappingMetamodel;
|
||||||
import org.hibernate.metamodel.mapping.Bindable;
|
import org.hibernate.metamodel.mapping.Bindable;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
|
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
@ -352,9 +353,6 @@ public class SqmUtil {
|
||||||
if ( parameterType == null ) {
|
if ( parameterType == null ) {
|
||||||
throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam );
|
throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam );
|
||||||
}
|
}
|
||||||
if ( parameterType instanceof CollectionPart && ( (CollectionPart) parameterType ).getPartMappingType() instanceof Bindable ) {
|
|
||||||
parameterType = (Bindable) ( (CollectionPart) parameterType ).getPartMappingType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( parameterType instanceof EntityIdentifierMapping ) {
|
if ( parameterType instanceof EntityIdentifierMapping ) {
|
||||||
final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType;
|
final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType;
|
||||||
|
@ -371,8 +369,8 @@ public class SqmUtil {
|
||||||
bindValue = identifierMapping.getIdentifier( bindValue );
|
bindValue = identifierMapping.getIdentifier( bindValue );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( parameterType instanceof ToOneAttributeMapping ) {
|
else if ( parameterType instanceof EntityAssociationMapping ) {
|
||||||
ToOneAttributeMapping association = (ToOneAttributeMapping) parameterType;
|
EntityAssociationMapping association = (EntityAssociationMapping) parameterType;
|
||||||
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide(
|
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide(
|
||||||
bindValue,
|
bindValue,
|
||||||
association.getSideNature().inverse(),
|
association.getSideNature().inverse(),
|
||||||
|
|
|
@ -248,6 +248,7 @@ import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
import org.hibernate.sql.ast.SqlTreeCreationException;
|
||||||
import org.hibernate.sql.ast.SqlTreeCreationLogger;
|
import org.hibernate.sql.ast.SqlTreeCreationLogger;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
|
@ -565,13 +566,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
// FromClauseAccess
|
// FromClauseAccess
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup findTableGroupOnLeaf(NavigablePath navigablePath) {
|
public TableGroup findTableGroup(NavigablePath navigablePath) {
|
||||||
return getFromClauseAccess().findTableGroupOnLeaf( navigablePath );
|
return getFromClauseAccess().findTableGroup( navigablePath );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup findTableGroup(NavigablePath navigablePath) {
|
public TableGroup findTableGroupOnParents(NavigablePath navigablePath) {
|
||||||
return getFromClauseAccess().findTableGroup( navigablePath );
|
return getFromClauseAccess().findTableGroupOnParents( navigablePath );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1753,6 +1754,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
protected void visitOrderByOffsetAndFetch(SqmQueryPart<?> sqmQueryPart, QueryPart sqlQueryPart) {
|
protected void visitOrderByOffsetAndFetch(SqmQueryPart<?> sqmQueryPart, QueryPart sqlQueryPart) {
|
||||||
if ( sqmQueryPart.getOrderByClause() != null ) {
|
if ( sqmQueryPart.getOrderByClause() != null ) {
|
||||||
currentClauseStack.push( Clause.ORDER );
|
currentClauseStack.push( Clause.ORDER );
|
||||||
|
inferrableTypeAccessStack.push( () -> null );
|
||||||
try {
|
try {
|
||||||
for ( SqmSortSpecification sortSpecification : sqmQueryPart.getOrderByClause()
|
for ( SqmSortSpecification sortSpecification : sqmQueryPart.getOrderByClause()
|
||||||
.getSortSpecifications() ) {
|
.getSortSpecifications() ) {
|
||||||
|
@ -1763,6 +1765,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
currentClauseStack.pop();
|
currentClauseStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1773,11 +1776,18 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
// case by using a subquery e.g. `... where alias in (select subAlias from ... limit ...)`
|
// case by using a subquery e.g. `... where alias in (select subAlias from ... limit ...)`
|
||||||
// or use window functions e.g. `select ... from (select ..., dense_rank() over(order by ..., id) rn from ...) tmp where tmp.rn between ...`
|
// or use window functions e.g. `select ... from (select ..., dense_rank() over(order by ..., id) rn from ...) tmp where tmp.rn between ...`
|
||||||
// but these transformations/translations are non-trivial and can be done later
|
// but these transformations/translations are non-trivial and can be done later
|
||||||
|
inferrableTypeAccessStack.push( () -> getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) );
|
||||||
sqlQueryPart.setOffsetClauseExpression( visitOffsetExpression( sqmQueryPart.getOffsetExpression() ) );
|
sqlQueryPart.setOffsetClauseExpression( visitOffsetExpression( sqmQueryPart.getOffsetExpression() ) );
|
||||||
|
if ( sqmQueryPart.getFetchClauseType() == FetchClauseType.PERCENT_ONLY
|
||||||
|
|| sqmQueryPart.getFetchClauseType() == FetchClauseType.PERCENT_WITH_TIES ) {
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
|
inferrableTypeAccessStack.push( () -> getTypeConfiguration().getBasicTypeForJavaType( Double.class ) );
|
||||||
|
}
|
||||||
sqlQueryPart.setFetchClauseExpression(
|
sqlQueryPart.setFetchClauseExpression(
|
||||||
visitFetchExpression( sqmQueryPart.getFetchExpression() ),
|
visitFetchExpression( sqmQueryPart.getFetchExpression() ),
|
||||||
sqmQueryPart.getFetchClauseType()
|
sqmQueryPart.getFetchClauseType()
|
||||||
);
|
);
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2065,6 +2075,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
||||||
if ( !groupByClauseExpressions.isEmpty() ) {
|
if ( !groupByClauseExpressions.isEmpty() ) {
|
||||||
currentClauseStack.push( Clause.GROUP );
|
currentClauseStack.push( Clause.GROUP );
|
||||||
|
inferrableTypeAccessStack.push( () -> null );
|
||||||
try {
|
try {
|
||||||
final List<Expression> expressions = new ArrayList<>( groupByClauseExpressions.size() );
|
final List<Expression> expressions = new ArrayList<>( groupByClauseExpressions.size() );
|
||||||
for ( SqmExpression<?> groupByClauseExpression : groupByClauseExpressions ) {
|
for ( SqmExpression<?> groupByClauseExpression : groupByClauseExpressions ) {
|
||||||
|
@ -2073,6 +2084,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return expressions;
|
return expressions;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
currentClauseStack.pop();
|
currentClauseStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2084,6 +2096,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
currentClauseStack.push( Clause.WHERE );
|
currentClauseStack.push( Clause.WHERE );
|
||||||
|
inferrableTypeAccessStack.push( () -> null );
|
||||||
try {
|
try {
|
||||||
return SqlAstTreeHelper.combinePredicates(
|
return SqlAstTreeHelper.combinePredicates(
|
||||||
(Predicate) sqmPredicate.accept( this ),
|
(Predicate) sqmPredicate.accept( this ),
|
||||||
|
@ -2091,6 +2104,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
currentClauseStack.pop();
|
currentClauseStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2101,6 +2115,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
currentClauseStack.push( Clause.HAVING );
|
currentClauseStack.push( Clause.HAVING );
|
||||||
|
inferrableTypeAccessStack.push( () -> null );
|
||||||
try {
|
try {
|
||||||
return SqlAstTreeHelper.combinePredicates(
|
return SqlAstTreeHelper.combinePredicates(
|
||||||
(Predicate) sqmPredicate.accept( this ),
|
(Predicate) sqmPredicate.accept( this ),
|
||||||
|
@ -2108,6 +2123,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
inferrableTypeAccessStack.pop();
|
||||||
currentClauseStack.pop();
|
currentClauseStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2170,6 +2186,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
currentClauseStack.push( Clause.FROM );
|
currentClauseStack.push( Clause.FROM );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// First, consume correlated roots, because these table groups can be used in join predicates of other from nodes
|
||||||
|
sqmFromClause.visitRoots( this::consumeFromClauseCorrelatedRoot );
|
||||||
sqmFromClause.visitRoots( this::consumeFromClauseRoot );
|
sqmFromClause.visitRoots( this::consumeFromClauseRoot );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -2179,6 +2197,185 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void consumeFromClauseCorrelatedRoot(SqmRoot<?> sqmRoot) {
|
||||||
|
log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot );
|
||||||
|
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
||||||
|
if ( fromClauseIndex.isResolved( sqmRoot ) ) {
|
||||||
|
log.tracef( "Already resolved SqmRoot [%s] to TableGroup", sqmRoot );
|
||||||
|
}
|
||||||
|
final QuerySpec currentQuerySpec = currentQuerySpec();
|
||||||
|
final TableGroup tableGroup;
|
||||||
|
if ( !sqmRoot.isCorrelated() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
||||||
|
if ( sqmRoot.containsOnlyInnerJoins() ) {
|
||||||
|
// If we have just inner joins against a correlated root, we can render the joins as references
|
||||||
|
final SqmFrom<?, ?> from;
|
||||||
|
// If we correlate a join, we have to create a special SqmRoot shell called SqmCorrelatedRootJoin.
|
||||||
|
// The only purpose of that is to serve as SqmRoot, which is needed for the FROM clause.
|
||||||
|
// It will always contain just a single correlated join though, which is what is actually correlated
|
||||||
|
if ( sqmRoot instanceof SqmCorrelatedRootJoin<?> ) {
|
||||||
|
assert sqmRoot.getSqmJoins().size() == 1;
|
||||||
|
assert sqmRoot.getSqmJoins().get( 0 ).isCorrelated();
|
||||||
|
from = sqmRoot.getSqmJoins().get( 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
from = sqmRoot;
|
||||||
|
}
|
||||||
|
final TableGroup parentTableGroup = fromClauseIndex.findTableGroupOnParents(
|
||||||
|
from.getCorrelationParent().getNavigablePath()
|
||||||
|
);
|
||||||
|
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() );
|
||||||
|
if ( parentTableGroup instanceof PluralTableGroup ) {
|
||||||
|
final PluralTableGroup pluralTableGroup = (PluralTableGroup) parentTableGroup;
|
||||||
|
final CorrelatedPluralTableGroup correlatedPluralTableGroup = new CorrelatedPluralTableGroup(
|
||||||
|
parentTableGroup,
|
||||||
|
sqlAliasBase,
|
||||||
|
currentQuerySpec,
|
||||||
|
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
predicate
|
||||||
|
),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
final TableGroup elementTableGroup = pluralTableGroup.getElementTableGroup();
|
||||||
|
if ( elementTableGroup != null ) {
|
||||||
|
final TableGroup correlatedElementTableGroup = new CorrelatedTableGroup(
|
||||||
|
elementTableGroup,
|
||||||
|
sqlAliasBase,
|
||||||
|
currentQuerySpec,
|
||||||
|
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
predicate
|
||||||
|
),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||||
|
elementTableGroup.getNavigablePath(),
|
||||||
|
SqlAstJoinType.INNER,
|
||||||
|
correlatedElementTableGroup
|
||||||
|
);
|
||||||
|
correlatedPluralTableGroup.registerElementTableGroup( tableGroupJoin );
|
||||||
|
}
|
||||||
|
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
|
||||||
|
if ( indexTableGroup != null ) {
|
||||||
|
final TableGroup correlatedIndexTableGroup = new CorrelatedTableGroup(
|
||||||
|
indexTableGroup,
|
||||||
|
sqlAliasBase,
|
||||||
|
currentQuerySpec,
|
||||||
|
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
predicate
|
||||||
|
),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||||
|
indexTableGroup.getNavigablePath(),
|
||||||
|
SqlAstJoinType.INNER,
|
||||||
|
correlatedIndexTableGroup
|
||||||
|
);
|
||||||
|
correlatedPluralTableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||||
|
}
|
||||||
|
tableGroup = correlatedPluralTableGroup;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tableGroup = new CorrelatedTableGroup(
|
||||||
|
parentTableGroup,
|
||||||
|
sqlAliasBase,
|
||||||
|
currentQuerySpec,
|
||||||
|
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
predicate
|
||||||
|
),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fromClauseIndex.register( from, tableGroup );
|
||||||
|
registerPluralTableGroupParts( tableGroup );
|
||||||
|
|
||||||
|
log.tracef( "Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup );
|
||||||
|
consumeExplicitJoins( from, tableGroup );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() );
|
||||||
|
final TableGroup parentTableGroup = fromClauseIndex.findTableGroupOnParents(
|
||||||
|
sqmRoot.getCorrelationParent().getNavigablePath()
|
||||||
|
);
|
||||||
|
// If we have non-inner joins against a correlated root, we must render the root with a correlation predicate
|
||||||
|
tableGroup = entityDescriptor.createRootTableGroup(
|
||||||
|
true,
|
||||||
|
sqmRoot.getNavigablePath(),
|
||||||
|
sqmRoot.getExplicitAlias(),
|
||||||
|
() -> predicate -> {},
|
||||||
|
this,
|
||||||
|
creationContext
|
||||||
|
);
|
||||||
|
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||||
|
final NavigablePath navigablePath = sqmRoot.getNavigablePath().append( identifierMapping.getNavigableRole().getNavigableName() );
|
||||||
|
final int jdbcTypeCount = identifierMapping.getJdbcTypeCount();
|
||||||
|
if ( jdbcTypeCount == 1 ) {
|
||||||
|
identifierMapping.forEachSelectable(
|
||||||
|
(index, selectable) -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
new ComparisonPredicate(
|
||||||
|
new ColumnReference(
|
||||||
|
parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
||||||
|
selectable,
|
||||||
|
sessionFactory
|
||||||
|
),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
new ColumnReference(
|
||||||
|
tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
||||||
|
selectable,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final List<Expression> lhs = new ArrayList<>( jdbcTypeCount );
|
||||||
|
final List<Expression> rhs = new ArrayList<>( jdbcTypeCount );
|
||||||
|
identifierMapping.forEachSelectable(
|
||||||
|
(index, selectable) -> {
|
||||||
|
lhs.add(
|
||||||
|
new ColumnReference(
|
||||||
|
parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
||||||
|
selectable,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
rhs.add(
|
||||||
|
new ColumnReference(
|
||||||
|
tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
||||||
|
selectable,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
|
additionalRestrictions,
|
||||||
|
new ComparisonPredicate(
|
||||||
|
new SqlTuple( lhs, identifierMapping ),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
new SqlTuple( rhs, identifierMapping )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.tracef( "Resolved SqmRoot [%s] to new TableGroup [%s]", sqmRoot, tableGroup );
|
||||||
|
|
||||||
|
fromClauseIndex.register( sqmRoot, tableGroup );
|
||||||
|
currentQuerySpec.getFromClause().addRoot( tableGroup );
|
||||||
|
|
||||||
|
consumeJoins( sqmRoot, fromClauseIndex, tableGroup );
|
||||||
|
}
|
||||||
|
|
||||||
protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) {
|
protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) {
|
||||||
log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot );
|
log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot );
|
||||||
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
||||||
|
@ -2188,195 +2385,39 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final QuerySpec currentQuerySpec = currentQuerySpec();
|
final QuerySpec currentQuerySpec = currentQuerySpec();
|
||||||
final TableGroup tableGroup;
|
final TableGroup tableGroup;
|
||||||
if ( sqmRoot.isCorrelated() ) {
|
if ( sqmRoot.isCorrelated() ) {
|
||||||
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
return;
|
||||||
final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() );
|
|
||||||
if ( sqmRoot.containsOnlyInnerJoins() ) {
|
|
||||||
// If we have just inner joins against a correlated root, we can render the joins as references
|
|
||||||
final SqmFrom<?, ?> from;
|
|
||||||
// If we correlate a join, we have to create a special SqmRoot shell called SqmCorrelatedRootJoin.
|
|
||||||
// The only purpose of that is to serve as SqmRoot, which is needed for the FROM clause.
|
|
||||||
// It will always contain just a single correlated join though, which is what is actually correlated
|
|
||||||
if ( sqmRoot instanceof SqmCorrelatedRootJoin<?> ) {
|
|
||||||
assert sqmRoot.getSqmJoins().size() == 1;
|
|
||||||
assert sqmRoot.getSqmJoins().get( 0 ).isCorrelated();
|
|
||||||
from = sqmRoot.getSqmJoins().get( 0 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
from = sqmRoot;
|
|
||||||
}
|
|
||||||
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
|
|
||||||
from.getCorrelationParent().getNavigablePath()
|
|
||||||
);
|
|
||||||
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() );
|
|
||||||
if ( parentTableGroup instanceof PluralTableGroup ) {
|
|
||||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) parentTableGroup;
|
|
||||||
final CorrelatedPluralTableGroup correlatedPluralTableGroup = new CorrelatedPluralTableGroup(
|
|
||||||
parentTableGroup,
|
|
||||||
sqlAliasBase,
|
|
||||||
currentQuerySpec,
|
|
||||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
predicate
|
|
||||||
),
|
|
||||||
sessionFactory
|
|
||||||
);
|
|
||||||
final TableGroup elementTableGroup = pluralTableGroup.getElementTableGroup();
|
|
||||||
if ( elementTableGroup != null ) {
|
|
||||||
final TableGroup correlatedElementTableGroup = new CorrelatedTableGroup(
|
|
||||||
elementTableGroup,
|
|
||||||
sqlAliasBase,
|
|
||||||
currentQuerySpec,
|
|
||||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
predicate
|
|
||||||
),
|
|
||||||
sessionFactory
|
|
||||||
);
|
|
||||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
|
||||||
elementTableGroup.getNavigablePath(),
|
|
||||||
SqlAstJoinType.INNER,
|
|
||||||
correlatedElementTableGroup
|
|
||||||
);
|
|
||||||
correlatedPluralTableGroup.registerElementTableGroup( tableGroupJoin );
|
|
||||||
}
|
|
||||||
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
|
|
||||||
if ( indexTableGroup != null ) {
|
|
||||||
final TableGroup correlatedIndexTableGroup = new CorrelatedTableGroup(
|
|
||||||
indexTableGroup,
|
|
||||||
sqlAliasBase,
|
|
||||||
currentQuerySpec,
|
|
||||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
predicate
|
|
||||||
),
|
|
||||||
sessionFactory
|
|
||||||
);
|
|
||||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
|
||||||
indexTableGroup.getNavigablePath(),
|
|
||||||
SqlAstJoinType.INNER,
|
|
||||||
correlatedIndexTableGroup
|
|
||||||
);
|
|
||||||
correlatedPluralTableGroup.registerIndexTableGroup( tableGroupJoin );
|
|
||||||
}
|
|
||||||
tableGroup = correlatedPluralTableGroup;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tableGroup = new CorrelatedTableGroup(
|
|
||||||
parentTableGroup,
|
|
||||||
sqlAliasBase,
|
|
||||||
currentQuerySpec,
|
|
||||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
predicate
|
|
||||||
),
|
|
||||||
sessionFactory
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fromClauseIndex.register( from, tableGroup );
|
|
||||||
registerPluralTableGroupParts( tableGroup );
|
|
||||||
|
|
||||||
log.tracef( "Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup );
|
|
||||||
consumeExplicitJoins( from, tableGroup );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
|
|
||||||
sqmRoot.getCorrelationParent().getNavigablePath()
|
|
||||||
);
|
|
||||||
// If we have non-inner joins against a correlated root, we must render the root with a correlation predicate
|
|
||||||
tableGroup = entityDescriptor.createRootTableGroup(
|
|
||||||
true,
|
|
||||||
sqmRoot.getNavigablePath(),
|
|
||||||
sqmRoot.getExplicitAlias(),
|
|
||||||
() -> predicate -> {},
|
|
||||||
this,
|
|
||||||
creationContext
|
|
||||||
);
|
|
||||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
|
||||||
final NavigablePath navigablePath = sqmRoot.getNavigablePath().append( identifierMapping.getNavigableRole().getNavigableName() );
|
|
||||||
final int jdbcTypeCount = identifierMapping.getJdbcTypeCount();
|
|
||||||
if ( jdbcTypeCount == 1 ) {
|
|
||||||
identifierMapping.forEachSelectable(
|
|
||||||
(index, selectable) -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
new ComparisonPredicate(
|
|
||||||
new ColumnReference(
|
|
||||||
parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
|
||||||
selectable,
|
|
||||||
sessionFactory
|
|
||||||
),
|
|
||||||
ComparisonOperator.EQUAL,
|
|
||||||
new ColumnReference(
|
|
||||||
tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
|
||||||
selectable,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final List<Expression> lhs = new ArrayList<>( jdbcTypeCount );
|
|
||||||
final List<Expression> rhs = new ArrayList<>( jdbcTypeCount );
|
|
||||||
identifierMapping.forEachSelectable(
|
|
||||||
(index, selectable) -> {
|
|
||||||
lhs.add(
|
|
||||||
new ColumnReference(
|
|
||||||
parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
|
||||||
selectable,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
);
|
|
||||||
rhs.add(
|
|
||||||
new ColumnReference(
|
|
||||||
tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
|
|
||||||
selectable,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
|
||||||
additionalRestrictions,
|
|
||||||
new ComparisonPredicate(
|
|
||||||
new SqlTuple( lhs, identifierMapping ),
|
|
||||||
ComparisonOperator.EQUAL,
|
|
||||||
new SqlTuple( rhs, identifierMapping )
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() );
|
||||||
final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() );
|
tableGroup = entityDescriptor.createRootTableGroup(
|
||||||
tableGroup = entityDescriptor.createRootTableGroup(
|
true,
|
||||||
true,
|
sqmRoot.getNavigablePath(),
|
||||||
sqmRoot.getNavigablePath(),
|
sqmRoot.getExplicitAlias(),
|
||||||
sqmRoot.getExplicitAlias(),
|
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||||
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
additionalRestrictions,
|
||||||
additionalRestrictions,
|
predicate
|
||||||
predicate
|
),
|
||||||
),
|
this,
|
||||||
this,
|
creationContext
|
||||||
creationContext
|
);
|
||||||
);
|
|
||||||
|
|
||||||
entityDescriptor.applyBaseRestrictions(
|
entityDescriptor.applyBaseRestrictions(
|
||||||
currentQuerySpec::applyPredicate,
|
currentQuerySpec::applyPredicate,
|
||||||
tableGroup,
|
tableGroup,
|
||||||
true,
|
true,
|
||||||
getLoadQueryInfluencers().getEnabledFilters(),
|
getLoadQueryInfluencers().getEnabledFilters(),
|
||||||
null,
|
null,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
log.tracef( "Resolved SqmRoot [%s] to new TableGroup [%s]", sqmRoot, tableGroup );
|
log.tracef( "Resolved SqmRoot [%s] to new TableGroup [%s]", sqmRoot, tableGroup );
|
||||||
|
|
||||||
fromClauseIndex.register( sqmRoot, tableGroup );
|
fromClauseIndex.register( sqmRoot, tableGroup );
|
||||||
currentQuerySpec.getFromClause().addRoot( tableGroup );
|
currentQuerySpec.getFromClause().addRoot( tableGroup );
|
||||||
|
|
||||||
|
consumeJoins( sqmRoot, fromClauseIndex, tableGroup );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void consumeJoins(SqmRoot<?> sqmRoot, FromClauseIndex fromClauseIndex, TableGroup tableGroup) {
|
||||||
if ( sqmRoot.getOrderedJoins() == null ) {
|
if ( sqmRoot.getOrderedJoins() == null ) {
|
||||||
consumeExplicitJoins( sqmRoot, tableGroup );
|
consumeExplicitJoins( sqmRoot, tableGroup );
|
||||||
}
|
}
|
||||||
|
@ -2401,7 +2442,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert ownerTableGroup != null;
|
assert ownerTableGroup != null;
|
||||||
lastTableGroup = consumeExplicitJoin( join, lastTableGroup, ownerTableGroup, false );
|
final TableGroup actualTableGroup = findActualTableGroup( ownerTableGroup, join );
|
||||||
|
lastTableGroup = consumeExplicitJoin( join, lastTableGroup, actualTableGroup, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2762,6 +2804,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
private <X> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) {
|
private <X> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) {
|
||||||
|
return prepareReusablePath( sqmPath, fromClauseIndexStack.getCurrent(), supplier );
|
||||||
|
}
|
||||||
|
|
||||||
|
private <X> X prepareReusablePath(SqmPath<?> sqmPath, FromClauseIndex fromClauseIndex, Supplier<X> supplier) {
|
||||||
final Consumer<TableGroup> implicitJoinChecker;
|
final Consumer<TableGroup> implicitJoinChecker;
|
||||||
if ( getCurrentProcessingState() instanceof SqlAstQueryPartProcessingState ) {
|
if ( getCurrentProcessingState() instanceof SqlAstQueryPartProcessingState ) {
|
||||||
implicitJoinChecker = tg -> {};
|
implicitJoinChecker = tg -> {};
|
||||||
|
@ -2769,7 +2815,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
else {
|
else {
|
||||||
implicitJoinChecker = BaseSqmToSqlAstConverter::verifyManipulationImplicitJoin;
|
implicitJoinChecker = BaseSqmToSqlAstConverter::verifyManipulationImplicitJoin;
|
||||||
}
|
}
|
||||||
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
|
||||||
prepareReusablePath( fromClauseIndex, sqmPath, implicitJoinChecker );
|
prepareReusablePath( fromClauseIndex, sqmPath, implicitJoinChecker );
|
||||||
return supplier.get();
|
return supplier.get();
|
||||||
}
|
}
|
||||||
|
@ -2795,6 +2840,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
parentPath,
|
parentPath,
|
||||||
implicitJoinChecker
|
implicitJoinChecker
|
||||||
);
|
);
|
||||||
|
if ( parentTableGroup == null ) {
|
||||||
|
final TableGroup parent = fromClauseIndex.findTableGroupOnParents( parentPath.getNavigablePath() );
|
||||||
|
if ( parent != null ) {
|
||||||
|
throw new SqlTreeCreationException( "Found un-correlated path usage in sub query - " + parentPath );
|
||||||
|
}
|
||||||
|
throw new SqlTreeCreationException( "Could not locate TableGroup - " + parentPath.getNavigablePath() );
|
||||||
|
}
|
||||||
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
|
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
|
||||||
fromClauseIndex.register( (SqmPath<?>) parentPath, parentTableGroup );
|
fromClauseIndex.register( (SqmPath<?>) parentPath, parentTableGroup );
|
||||||
return parentTableGroup;
|
return parentTableGroup;
|
||||||
|
@ -2877,9 +2929,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final TableGroup tableGroup;
|
final TableGroup tableGroup;
|
||||||
if ( subPart instanceof TableGroupJoinProducer ) {
|
if ( subPart instanceof TableGroupJoinProducer ) {
|
||||||
final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) subPart;
|
final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) subPart;
|
||||||
final SqlAstJoinType defaultSqlAstJoinType;
|
if ( fromClauseIndex.findTableGroup( actualParentTableGroup.getNavigablePath() ) == null ) {
|
||||||
|
|
||||||
if ( fromClauseIndex.findTableGroupOnLeaf( actualParentTableGroup.getNavigablePath() ) == null ) {
|
|
||||||
final QuerySpec querySpec = currentQuerySpec();
|
final QuerySpec querySpec = currentQuerySpec();
|
||||||
// The parent table group is on a parent query, so we need a root table group
|
// The parent table group is on a parent query, so we need a root table group
|
||||||
tableGroup = joinProducer.createRootTableGroupJoin(
|
tableGroup = joinProducer.createRootTableGroupJoin(
|
||||||
|
@ -3056,47 +3106,97 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
|
private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
|
||||||
final ModelPartContainer modelPart = tableGroup.getModelPart();
|
final ModelPartContainer modelPart;
|
||||||
final ModelPart keyPart;
|
final MappingModelExpressible<?> inferredValueMapping = getInferredValueMapping();
|
||||||
final ModelPart resultPart;
|
// For plain SqmFrom node uses, prefer the mapping type from the context if possible
|
||||||
|
if ( !( inferredValueMapping instanceof ModelPartContainer ) ) {
|
||||||
|
modelPart = tableGroup.getModelPart();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
modelPart = (ModelPartContainer) inferredValueMapping;
|
||||||
|
}
|
||||||
|
final ModelPart resultModelPart;
|
||||||
|
final ModelPart interpretationModelPart;
|
||||||
|
final TableGroup parentGroupToUse;
|
||||||
if ( modelPart instanceof ToOneAttributeMapping ) {
|
if ( modelPart instanceof ToOneAttributeMapping ) {
|
||||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) modelPart;
|
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) modelPart;
|
||||||
keyPart = toOneAttributeMapping.findSubPart( toOneAttributeMapping.getTargetKeyPropertyName() );
|
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(
|
||||||
resultPart = modelPart;
|
toOneAttributeMapping.getSideNature().inverse()
|
||||||
|
);
|
||||||
|
if ( tableGroup.getModelPart().getPartMappingType() == modelPart.getPartMappingType() ) {
|
||||||
|
resultModelPart = targetPart;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If the table group is for a different mapping type i.e. an inheritance subtype,
|
||||||
|
// lookup the target part on that mapping type
|
||||||
|
resultModelPart = tableGroup.getModelPart().findSubPart( targetPart.getPartName(), null );
|
||||||
|
}
|
||||||
|
interpretationModelPart = modelPart;
|
||||||
|
parentGroupToUse = null;
|
||||||
}
|
}
|
||||||
else if ( modelPart instanceof PluralAttributeMapping ) {
|
else if ( modelPart instanceof PluralAttributeMapping ) {
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart;
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart;
|
||||||
final CollectionPart elementDescriptor = pluralAttributeMapping.getElementDescriptor();
|
final CollectionPart elementDescriptor = pluralAttributeMapping.getElementDescriptor();
|
||||||
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
||||||
keyPart = ( (EntityCollectionPart) elementDescriptor ).getKeyTargetMatchPart();
|
// Usually, we need to resolve to the PK for visitTableGroup
|
||||||
|
final EntityCollectionPart collectionPart = (EntityCollectionPart) elementDescriptor;
|
||||||
|
final ModelPart collectionTargetPart = collectionPart.getForeignKeyDescriptor()
|
||||||
|
.getPart( collectionPart.getSideNature().inverse() );
|
||||||
|
final EntityIdentifierMapping identifierMapping = collectionPart.getEntityMappingType()
|
||||||
|
.getIdentifierMapping();
|
||||||
|
// If the FK points to the PK, we can use the FK part though, if this is not a root
|
||||||
|
if ( collectionTargetPart == identifierMapping && !( path instanceof SqmRoot<?> ) ) {
|
||||||
|
resultModelPart = collectionPart.getForeignKeyDescriptor().getPart( collectionPart.getSideNature() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultModelPart = identifierMapping;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
keyPart = elementDescriptor;
|
resultModelPart = elementDescriptor;
|
||||||
}
|
}
|
||||||
resultPart = elementDescriptor;
|
interpretationModelPart = elementDescriptor;
|
||||||
|
parentGroupToUse = null;
|
||||||
}
|
}
|
||||||
else if ( modelPart instanceof EntityCollectionPart ) {
|
else if ( modelPart instanceof EntityCollectionPart ) {
|
||||||
keyPart = ( (EntityCollectionPart) modelPart ).getKeyTargetMatchPart();
|
// Usually, we need to resolve to the PK for visitTableGroup
|
||||||
resultPart = modelPart;
|
final EntityCollectionPart collectionPart = (EntityCollectionPart) modelPart;
|
||||||
|
final ModelPart collectionTargetPart = collectionPart.getForeignKeyDescriptor()
|
||||||
|
.getPart( collectionPart.getSideNature().inverse() );
|
||||||
|
final EntityIdentifierMapping identifierMapping = collectionPart.getEntityMappingType()
|
||||||
|
.getIdentifierMapping();
|
||||||
|
// If the FK points to the PK, we can use the FK part though, if this is not a root
|
||||||
|
if ( collectionTargetPart == identifierMapping && !( path instanceof SqmRoot<?> ) ) {
|
||||||
|
resultModelPart = collectionPart.getForeignKeyDescriptor().getPart( collectionPart.getSideNature() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultModelPart = identifierMapping;
|
||||||
|
}
|
||||||
|
interpretationModelPart = modelPart;
|
||||||
|
parentGroupToUse = findTableGroup( tableGroup.getNavigablePath().getParent() );
|
||||||
}
|
}
|
||||||
else if ( modelPart instanceof EntityMappingType ) {
|
else if ( modelPart instanceof EntityMappingType ) {
|
||||||
keyPart = ( (EntityMappingType) modelPart ).getIdentifierMapping();
|
resultModelPart = ( (EntityMappingType) modelPart ).getIdentifierMapping();
|
||||||
resultPart = modelPart;
|
interpretationModelPart = modelPart;
|
||||||
|
// todo: I think this will always be null anyways because EntityMappingType will only be the model part
|
||||||
|
// of a TableGroup if that is a root TableGroup, so check if we can just switch to null
|
||||||
|
parentGroupToUse = findTableGroup( tableGroup.getNavigablePath().getParent() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
keyPart = modelPart;
|
resultModelPart = modelPart;
|
||||||
resultPart = modelPart;
|
interpretationModelPart = modelPart;
|
||||||
|
parentGroupToUse = null;
|
||||||
}
|
}
|
||||||
final NavigablePath navigablePath;
|
final NavigablePath navigablePath;
|
||||||
if ( resultPart == modelPart ) {
|
if ( interpretationModelPart == modelPart ) {
|
||||||
navigablePath = tableGroup.getNavigablePath();
|
navigablePath = tableGroup.getNavigablePath();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
navigablePath = tableGroup.getNavigablePath().append( resultPart.getPartName() );
|
navigablePath = tableGroup.getNavigablePath().append( interpretationModelPart.getPartName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
final Expression result;
|
final Expression result;
|
||||||
if ( resultPart instanceof EntityValuedModelPart ) {
|
if ( interpretationModelPart instanceof EntityValuedModelPart ) {
|
||||||
final boolean expandToAllColumns;
|
final boolean expandToAllColumns;
|
||||||
if ( currentClauseStack.getCurrent() == Clause.GROUP ) {
|
if ( currentClauseStack.getCurrent() == Clause.GROUP ) {
|
||||||
// When the table group is known to be fetched i.e. a fetch join
|
// When the table group is known to be fetched i.e. a fetch join
|
||||||
|
@ -3108,7 +3208,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
expandToAllColumns = false;
|
expandToAllColumns = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) resultPart;
|
final EntityValuedModelPart mapping = (EntityValuedModelPart) interpretationModelPart;
|
||||||
EntityMappingType mappingType;
|
EntityMappingType mappingType;
|
||||||
if ( path instanceof SqmTreatedPath ) {
|
if ( path instanceof SqmTreatedPath ) {
|
||||||
mappingType = creationContext.getSessionFactory()
|
mappingType = creationContext.getSessionFactory()
|
||||||
|
@ -3120,18 +3220,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
mappingType = mapping.getEntityMappingType();
|
mappingType = mapping.getEntityMappingType();
|
||||||
}
|
}
|
||||||
|
|
||||||
final TableGroup parentGroupToUse = findTableGroup( navigablePath.getParent() );
|
|
||||||
result = EntityValuedPathInterpretation.from(
|
result = EntityValuedPathInterpretation.from(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
parentGroupToUse == null ? tableGroup : parentGroupToUse,
|
parentGroupToUse == null ? tableGroup : parentGroupToUse,
|
||||||
(EntityValuedModelPart) resultPart,
|
expandToAllColumns ? null : resultModelPart,
|
||||||
|
(EntityValuedModelPart) interpretationModelPart,
|
||||||
mappingType,
|
mappingType,
|
||||||
expandToAllColumns,
|
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( resultPart instanceof EmbeddableValuedModelPart ) {
|
else if ( interpretationModelPart instanceof EmbeddableValuedModelPart ) {
|
||||||
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) keyPart;
|
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) resultModelPart;
|
||||||
result = new EmbeddableValuedPathInterpretation<>(
|
result = new EmbeddableValuedPathInterpretation<>(
|
||||||
mapping.toSqlExpression(
|
mapping.toSqlExpression(
|
||||||
tableGroup,
|
tableGroup,
|
||||||
|
@ -3140,15 +3239,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
getSqlAstCreationState()
|
getSqlAstCreationState()
|
||||||
),
|
),
|
||||||
navigablePath,
|
navigablePath,
|
||||||
(EmbeddableValuedModelPart) resultPart,
|
(EmbeddableValuedModelPart) interpretationModelPart,
|
||||||
tableGroup
|
tableGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert resultPart instanceof BasicValuedModelPart;
|
assert interpretationModelPart instanceof BasicValuedModelPart;
|
||||||
final BasicValuedModelPart mapping = (BasicValuedModelPart) keyPart;
|
final BasicValuedModelPart mapping = (BasicValuedModelPart) resultModelPart;
|
||||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
navigablePath.append( keyPart.getPartName() ),
|
navigablePath.append( resultModelPart.getPartName() ),
|
||||||
mapping.getContainingTableExpression()
|
mapping.getContainingTableExpression()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3178,7 +3277,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
result = new BasicValuedPathInterpretation<>(
|
result = new BasicValuedPathInterpretation<>(
|
||||||
columnReference,
|
columnReference,
|
||||||
navigablePath,
|
navigablePath,
|
||||||
(BasicValuedModelPart) resultPart,
|
(BasicValuedModelPart) interpretationModelPart,
|
||||||
tableGroup
|
tableGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3328,7 +3427,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
@Override
|
@Override
|
||||||
public Expression visitEntityValuedPath(SqmEntityValuedSimplePath<?> sqmPath) {
|
public Expression visitEntityValuedPath(SqmEntityValuedSimplePath<?> sqmPath) {
|
||||||
return withTreatRestriction(
|
return withTreatRestriction(
|
||||||
prepareReusablePath( sqmPath, () -> EntityValuedPathInterpretation.from( sqmPath, this ) ),
|
prepareReusablePath( sqmPath, () -> EntityValuedPathInterpretation.from( sqmPath, getInferredValueMapping(), this ) ),
|
||||||
sqmPath
|
sqmPath
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4447,19 +4546,23 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MappingModelExpressible<?> determineValueMapping(SqmExpression<?> sqmExpression) {
|
public MappingModelExpressible<?> determineValueMapping(SqmExpression<?> sqmExpression) {
|
||||||
|
return determineValueMapping( sqmExpression, fromClauseIndexStack.getCurrent() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MappingModelExpressible<?> determineValueMapping(SqmExpression<?> sqmExpression, FromClauseIndex fromClauseIndex) {
|
||||||
if ( sqmExpression instanceof SqmParameter ) {
|
if ( sqmExpression instanceof SqmParameter ) {
|
||||||
return determineValueMapping( (SqmParameter<?>) sqmExpression );
|
return determineValueMapping( (SqmParameter<?>) sqmExpression );
|
||||||
}
|
}
|
||||||
else if ( sqmExpression instanceof SqmPath ) {
|
else if ( sqmExpression instanceof SqmPath ) {
|
||||||
log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression );
|
log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression );
|
||||||
prepareReusablePath( (SqmPath<?>) sqmExpression, () -> null );
|
prepareReusablePath( (SqmPath<?>) sqmExpression, fromClauseIndex, () -> null );
|
||||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||||
.getRuntimeMetamodels()
|
.getRuntimeMetamodels()
|
||||||
.getMappingMetamodel();
|
.getMappingMetamodel();
|
||||||
return SqmMappingModelHelper.resolveMappingModelExpressible(
|
return SqmMappingModelHelper.resolveMappingModelExpressible(
|
||||||
sqmExpression,
|
sqmExpression,
|
||||||
domainModel,
|
domainModel,
|
||||||
getFromClauseAccess()::findTableGroup
|
fromClauseIndex::findTableGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// The model type of an enum literal is always inferred
|
// The model type of an enum literal is always inferred
|
||||||
|
@ -4492,12 +4595,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final SqmPath<?> sqmPath = (SqmPath<?>) subQuerySelection.getSelectableNode();
|
final SqmPath<?> sqmPath = (SqmPath<?>) subQuerySelection.getSelectableNode();
|
||||||
final NavigablePath navigablePath = sqmPath.getNavigablePath().getParent();
|
final NavigablePath navigablePath = sqmPath.getNavigablePath().getParent();
|
||||||
if ( navigablePath.getParent() != null ) {
|
if ( navigablePath.getParent() != null ) {
|
||||||
final TableGroup parentTableGroup = findTableGroup( navigablePath.getParent() );
|
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup( navigablePath.getParent() );
|
||||||
final PluralAttributeMapping pluralPart = (PluralAttributeMapping) parentTableGroup.getModelPart()
|
final PluralAttributeMapping pluralPart = (PluralAttributeMapping) parentTableGroup.getModelPart()
|
||||||
.findSubPart( navigablePath.getUnaliasedLocalName(), null );
|
.findSubPart( navigablePath.getUnaliasedLocalName(), null );
|
||||||
return pluralPart.findSubPart( pathSource.getPathName(), null );
|
return pluralPart.findSubPart( pathSource.getPathName(), null );
|
||||||
}
|
}
|
||||||
return findTableGroup( navigablePath ).getModelPart();
|
return fromClauseIndex.findTableGroup( navigablePath ).getModelPart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -4537,7 +4640,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
.getMappingMetamodel();
|
.getMappingMetamodel();
|
||||||
final MappingModelExpressible<?> valueMapping = domainModel.resolveMappingExpressible(
|
final MappingModelExpressible<?> valueMapping = domainModel.resolveMappingExpressible(
|
||||||
nodeType,
|
nodeType,
|
||||||
this::findTableGroupByPath
|
fromClauseIndex::getTableGroup
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( valueMapping == null ) {
|
if ( valueMapping == null ) {
|
||||||
|
@ -4548,6 +4651,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( valueMapping == null ) {
|
if ( valueMapping == null ) {
|
||||||
|
// For literals it is totally possible that we can't figure out a mapping type
|
||||||
|
if ( sqmExpression instanceof SqmLiteral<?> ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
throw new ConversionException( "Could not determine ValueMapping for SqmExpression: " + sqmExpression );
|
throw new ConversionException( "Could not determine ValueMapping for SqmExpression: " + sqmExpression );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4558,7 +4665,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final MappingModelExpressible<?> inferredMapping = resolveInferredType();
|
final MappingModelExpressible<?> inferredMapping = resolveInferredType();
|
||||||
if ( inferredMapping != null ) {
|
if ( inferredMapping != null ) {
|
||||||
if ( inferredMapping instanceof PluralAttributeMapping ) {
|
if ( inferredMapping instanceof PluralAttributeMapping ) {
|
||||||
return ( (PluralAttributeMapping) inferredMapping ).getElementDescriptor();
|
final CollectionPart elementDescriptor = ( (PluralAttributeMapping) inferredMapping ).getElementDescriptor();
|
||||||
|
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
||||||
|
return ( (EntityCollectionPart) elementDescriptor ).getEntityMappingType();
|
||||||
|
}
|
||||||
|
return elementDescriptor;
|
||||||
}
|
}
|
||||||
else if ( !( inferredMapping instanceof JavaObjectType ) ) {
|
else if ( !( inferredMapping instanceof JavaObjectType ) ) {
|
||||||
// Never report back the "object type" as inferred type and instead rely on the value type
|
// Never report back the "object type" as inferred type and instead rely on the value type
|
||||||
|
@ -4602,6 +4713,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return inferredValueMapping;
|
return inferredValueMapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( paramType instanceof EntityDomainType ) {
|
||||||
|
// In JPA Criteria, it is possible to define a parameter of an entity type,
|
||||||
|
// but that should infer the mapping type from context,
|
||||||
|
// otherwise this would default to binding the PK which might be wrong
|
||||||
|
final MappingModelExpressible<?> inferredValueMapping = getInferredValueMapping();
|
||||||
|
if ( inferredValueMapping != null ) {
|
||||||
|
return inferredValueMapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final SqmExpressible<?> paramSqmType = paramType.resolveExpressible( creationContext.getSessionFactory() );
|
final SqmExpressible<?> paramSqmType = paramType.resolveExpressible( creationContext.getSessionFactory() );
|
||||||
|
|
||||||
|
@ -4653,7 +4773,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
sqmParameterMappingModelTypes.put( expression, valueMapping );
|
sqmParameterMappingModelTypes.put( expression, valueMapping );
|
||||||
final Bindable bindable;
|
final Bindable bindable;
|
||||||
if ( valueMapping instanceof EntityAssociationMapping ) {
|
if ( valueMapping instanceof EntityAssociationMapping ) {
|
||||||
bindable = ( (EntityAssociationMapping) valueMapping ).getKeyTargetMatchPart();
|
final EntityAssociationMapping mapping = (EntityAssociationMapping) valueMapping;
|
||||||
|
bindable = mapping.getForeignKeyDescriptor().getPart( mapping.getSideNature() );
|
||||||
}
|
}
|
||||||
else if ( valueMapping instanceof EntityMappingType ) {
|
else if ( valueMapping instanceof EntityMappingType ) {
|
||||||
bindable = ( (EntityMappingType) valueMapping ).getIdentifierMapping();
|
bindable = ( (EntityMappingType) valueMapping ).getIdentifierMapping();
|
||||||
|
@ -4905,10 +5026,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Infer one operand type through the other
|
// Infer one operand type through the other
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand ) );
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
|
inferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand, fromClauseIndex ) );
|
||||||
final Expression lhs = toSqlExpression( leftOperand.accept( this ) );
|
final Expression lhs = toSqlExpression( leftOperand.accept( this ) );
|
||||||
inferrableTypeAccessStack.pop();
|
inferrableTypeAccessStack.pop();
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( leftOperand ) );
|
inferrableTypeAccessStack.push( () -> determineValueMapping( leftOperand, fromClauseIndex ) );
|
||||||
final Expression rhs = toSqlExpression( rightOperand.accept( this ) );
|
final Expression rhs = toSqlExpression( rightOperand.accept( this ) );
|
||||||
inferrableTypeAccessStack.pop();
|
inferrableTypeAccessStack.pop();
|
||||||
|
|
||||||
|
@ -5435,7 +5557,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
private <X> X visitWithInferredType(SqmExpression<?> expression, SqmExpression<?> inferred) {
|
private <X> X visitWithInferredType(SqmExpression<?> expression, SqmExpression<?> inferred) {
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( inferred ) );
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
|
inferrableTypeAccessStack.push( () -> determineValueMapping( inferred, fromClauseIndex ) );
|
||||||
try {
|
try {
|
||||||
return (X) expression.accept( this );
|
return (X) expression.accept( this );
|
||||||
}
|
}
|
||||||
|
@ -5762,7 +5885,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ComparisonPredicate visitComparisonPredicate(SqmComparisonPredicate predicate) {
|
public ComparisonPredicate visitComparisonPredicate(SqmComparisonPredicate predicate) {
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getRightHandExpression() ) );
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
|
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getRightHandExpression(), fromClauseIndex ) );
|
||||||
|
|
||||||
final Expression lhs;
|
final Expression lhs;
|
||||||
try {
|
try {
|
||||||
|
@ -5772,7 +5896,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
inferrableTypeAccessStack.pop();
|
inferrableTypeAccessStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getLeftHandExpression() ) );
|
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getLeftHandExpression(), fromClauseIndex ) );
|
||||||
|
|
||||||
final Expression rhs;
|
final Expression rhs;
|
||||||
try {
|
try {
|
||||||
|
@ -5862,14 +5986,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BetweenPredicate visitBetweenPredicate(SqmBetweenPredicate predicate) {
|
public BetweenPredicate visitBetweenPredicate(SqmBetweenPredicate predicate) {
|
||||||
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
final Expression expression;
|
final Expression expression;
|
||||||
final Expression lowerBound;
|
final Expression lowerBound;
|
||||||
final Expression upperBound;
|
final Expression upperBound;
|
||||||
|
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> coalesceSuppliedValues(
|
() -> coalesceSuppliedValues(
|
||||||
() -> determineValueMapping( predicate.getLowerBound() ),
|
() -> determineValueMapping( predicate.getLowerBound(), fromClauseIndex ),
|
||||||
() -> determineValueMapping( predicate.getUpperBound() )
|
() -> determineValueMapping( predicate.getUpperBound(), fromClauseIndex )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5882,8 +6007,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> coalesceSuppliedValues(
|
() -> coalesceSuppliedValues(
|
||||||
() -> determineValueMapping( predicate.getExpression() ),
|
() -> determineValueMapping( predicate.getExpression(), fromClauseIndex ),
|
||||||
() -> determineValueMapping( predicate.getUpperBound() )
|
() -> determineValueMapping( predicate.getUpperBound(), fromClauseIndex )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
|
@ -5895,8 +6020,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> coalesceSuppliedValues(
|
() -> coalesceSuppliedValues(
|
||||||
() -> determineValueMapping( predicate.getExpression() ),
|
() -> determineValueMapping( predicate.getExpression(), fromClauseIndex ),
|
||||||
() -> determineValueMapping( predicate.getLowerBound() )
|
() -> determineValueMapping( predicate.getLowerBound(), fromClauseIndex )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
|
@ -5960,10 +6085,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise - no special case...
|
// otherwise - no special case...
|
||||||
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> {
|
() -> {
|
||||||
for ( SqmExpression<?> listExpression : predicate.getListExpressions() ) {
|
for ( SqmExpression<?> listExpression : predicate.getListExpressions() ) {
|
||||||
final MappingModelExpressible<?> mapping = determineValueMapping( listExpression );
|
final MappingModelExpressible<?> mapping = determineValueMapping( listExpression, fromClauseIndex );
|
||||||
if ( mapping != null ) {
|
if ( mapping != null ) {
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
@ -5985,7 +6111,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
getBooleanType()
|
getBooleanType()
|
||||||
);
|
);
|
||||||
|
|
||||||
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getTestExpression() ) );
|
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getTestExpression(), fromClauseIndex ) );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
|
for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
|
||||||
|
@ -6052,8 +6178,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return inListPredicate;
|
return inListPredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> determineValueMapping( sqmPredicate.getTestExpression() )
|
() -> determineValueMapping( sqmPredicate.getTestExpression(), fromClauseIndex )
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -12,13 +12,13 @@ import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
@ -46,22 +46,67 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
|
|
||||||
public static <T> EntityValuedPathInterpretation<T> from(
|
public static <T> EntityValuedPathInterpretation<T> from(
|
||||||
SqmEntityValuedSimplePath<T> sqmPath,
|
SqmEntityValuedSimplePath<T> sqmPath,
|
||||||
|
MappingModelExpressible<?> inferredMapping,
|
||||||
SqmToSqlAstConverter sqlAstCreationState) {
|
SqmToSqlAstConverter sqlAstCreationState) {
|
||||||
final TableGroup tableGroup = sqlAstCreationState
|
final TableGroup tableGroup = sqlAstCreationState
|
||||||
.getFromClauseAccess()
|
.getFromClauseAccess()
|
||||||
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
|
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
|
||||||
|
final EntityValuedModelPart pathMapping = (EntityValuedModelPart) sqlAstCreationState
|
||||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) sqlAstCreationState
|
|
||||||
.getFromClauseAccess()
|
.getFromClauseAccess()
|
||||||
.findTableGroup( sqmPath.getLhs().getNavigablePath() )
|
.findTableGroup( sqmPath.getLhs().getNavigablePath() )
|
||||||
.getModelPart()
|
.getModelPart()
|
||||||
.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
||||||
|
final EntityValuedModelPart mapping;
|
||||||
|
if ( inferredMapping instanceof EntityAssociationMapping ) {
|
||||||
|
final EntityAssociationMapping inferredAssociation = (EntityAssociationMapping) inferredMapping;
|
||||||
|
if ( pathMapping instanceof EntityAssociationMapping && inferredMapping != pathMapping ) {
|
||||||
|
// In here, the inferred mapping and the actual path mapping are association mappings,
|
||||||
|
// but for different associations, so we have to check if both associations point to the same target
|
||||||
|
final EntityAssociationMapping pathAssociation = (EntityAssociationMapping) pathMapping;
|
||||||
|
final ModelPart pathTargetPart = pathAssociation.getForeignKeyDescriptor()
|
||||||
|
.getPart( pathAssociation.getSideNature().inverse() );
|
||||||
|
final ModelPart inferredTargetPart = inferredAssociation.getForeignKeyDescriptor()
|
||||||
|
.getPart( inferredAssociation.getSideNature().inverse() );
|
||||||
|
// If the inferred association and path association targets are the same, we can use the path mapping type
|
||||||
|
// which will render the FK of the path association
|
||||||
|
if ( pathTargetPart == inferredTargetPart ) {
|
||||||
|
mapping = pathMapping;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, we need to use the entity mapping type to force rendering the PK
|
||||||
|
// for e.g. `a.assoc1 = a.assoc2` when both associations have different target join columns
|
||||||
|
mapping = pathMapping.getEntityMappingType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This is the case when the inferred mapping is an association, but the path mapping is not,
|
||||||
|
// or the path mapping and the inferred mapping are for the same association
|
||||||
|
mapping = (EntityValuedModelPart) inferredMapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mapping = pathMapping;
|
||||||
|
}
|
||||||
|
final ModelPart resultModelPart;
|
||||||
|
if ( mapping instanceof EntityAssociationMapping ) {
|
||||||
|
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
|
||||||
|
final ModelPart keyTargetMatchPart = associationMapping.getKeyTargetMatchPart();
|
||||||
|
if ( keyTargetMatchPart instanceof ToOneAttributeMapping ) {
|
||||||
|
resultModelPart = ( (ToOneAttributeMapping) keyTargetMatchPart ).getKeyTargetMatchPart();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultModelPart = keyTargetMatchPart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultModelPart = mapping.getEntityMappingType().getIdentifierMapping();
|
||||||
|
}
|
||||||
return from(
|
return from(
|
||||||
sqmPath.getNavigablePath(),
|
sqmPath.getNavigablePath(),
|
||||||
tableGroup,
|
tableGroup,
|
||||||
|
resultModelPart,
|
||||||
mapping,
|
mapping,
|
||||||
mapping,
|
mapping,
|
||||||
false,
|
|
||||||
sqlAstCreationState
|
sqlAstCreationState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -69,15 +114,15 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
public static <T> EntityValuedPathInterpretation<T> from(
|
public static <T> EntityValuedPathInterpretation<T> from(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup tableGroup,
|
TableGroup tableGroup,
|
||||||
|
ModelPart resultModelPart,
|
||||||
EntityValuedModelPart mapping,
|
EntityValuedModelPart mapping,
|
||||||
EntityValuedModelPart treatedMapping,
|
EntityValuedModelPart treatedMapping,
|
||||||
boolean expandToAllColumns,
|
|
||||||
SqmToSqlAstConverter sqlAstCreationState) {
|
SqmToSqlAstConverter sqlAstCreationState) {
|
||||||
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||||
final SessionFactoryImplementor sessionFactory = sqlAstCreationState.getCreationContext().getSessionFactory();
|
final SessionFactoryImplementor sessionFactory = sqlAstCreationState.getCreationContext().getSessionFactory();
|
||||||
final Expression sqlExpression;
|
final Expression sqlExpression;
|
||||||
|
|
||||||
if ( expandToAllColumns ) {
|
if ( resultModelPart == null ) {
|
||||||
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
||||||
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||||
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
||||||
|
@ -113,19 +158,9 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
entityMappingType.forEachSelectable( selectableConsumer );
|
entityMappingType.forEachSelectable( selectableConsumer );
|
||||||
sqlExpression = new SqlTuple( expressions, entityMappingType );
|
sqlExpression = new SqlTuple( expressions, entityMappingType );
|
||||||
}
|
}
|
||||||
else if ( mapping instanceof EntityAssociationMapping ) {
|
else {
|
||||||
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
|
if ( resultModelPart instanceof BasicValuedModelPart ) {
|
||||||
final ModelPart keyTargetMatchPart = associationMapping.getKeyTargetMatchPart();
|
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) resultModelPart;
|
||||||
final ModelPart lhsPart;
|
|
||||||
if ( keyTargetMatchPart instanceof ToOneAttributeMapping ) {
|
|
||||||
lhsPart = ( (ToOneAttributeMapping) keyTargetMatchPart ).getKeyTargetMatchPart();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lhsPart = keyTargetMatchPart;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( lhsPart instanceof BasicValuedModelPart ) {
|
|
||||||
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) lhsPart;
|
|
||||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
basicValuedModelPart.getContainingTableExpression()
|
basicValuedModelPart.getContainingTableExpression()
|
||||||
|
@ -140,8 +175,8 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final List<Expression> expressions = new ArrayList<>( lhsPart.getJdbcTypeCount() );
|
final List<Expression> expressions = new ArrayList<>( resultModelPart.getJdbcTypeCount() );
|
||||||
lhsPart.forEachSelectable(
|
resultModelPart.forEachSelectable(
|
||||||
(selectionIndex, selectableMapping) -> {
|
(selectionIndex, selectableMapping) -> {
|
||||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
|
@ -162,59 +197,9 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
sqlExpression = new SqlTuple( expressions, lhsPart );
|
sqlExpression = new SqlTuple( expressions, resultModelPart );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
assert mapping instanceof EntityMappingType;
|
|
||||||
|
|
||||||
final TableGroup parentTableGroup = tableGroup;
|
|
||||||
final EntityMappingType entityMappingType = (EntityMappingType) mapping;
|
|
||||||
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
|
||||||
if ( identifierMapping instanceof BasicEntityIdentifierMapping ) {
|
|
||||||
final BasicEntityIdentifierMapping simpleIdMapping = (BasicEntityIdentifierMapping) identifierMapping;
|
|
||||||
|
|
||||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
|
||||||
navigablePath,
|
|
||||||
simpleIdMapping.getContainingTableExpression()
|
|
||||||
);
|
|
||||||
assert tableReference != null : "Could not resolve table-group : " + simpleIdMapping.getContainingTableExpression();
|
|
||||||
|
|
||||||
sqlExpression = sqlExprResolver.resolveSqlExpression(
|
|
||||||
createColumnReferenceKey( tableReference, simpleIdMapping.getSelectionExpression() ),
|
|
||||||
processingState -> new ColumnReference(
|
|
||||||
tableReference,
|
|
||||||
simpleIdMapping,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final List<Expression> expressions = new ArrayList<>();
|
|
||||||
identifierMapping.forEachSelectable(
|
|
||||||
(selectionIndex, selectableMapping) -> {
|
|
||||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
|
||||||
navigablePath, selectableMapping.getContainingTableExpression() );
|
|
||||||
|
|
||||||
expressions.add(
|
|
||||||
sqlExprResolver.resolveSqlExpression(
|
|
||||||
createColumnReferenceKey(
|
|
||||||
tableReference,
|
|
||||||
selectableMapping.getSelectionExpression()
|
|
||||||
),
|
|
||||||
processingState -> new ColumnReference(
|
|
||||||
tableReference,
|
|
||||||
selectableMapping,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
sqlExpression = new SqlTuple( expressions, identifierMapping );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EntityValuedPathInterpretation<>(
|
return new EntityValuedPathInterpretation<>(
|
||||||
sqlExpression,
|
sqlExpression,
|
||||||
navigablePath,
|
navigablePath,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.util.function.Function;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.query.BindableType;
|
import org.hibernate.query.BindableType;
|
||||||
|
@ -51,7 +52,11 @@ public class SqmParameterInterpretation implements Expression, DomainResultProdu
|
||||||
this.queryParameter = queryParameter;
|
this.queryParameter = queryParameter;
|
||||||
this.queryParameterBindingResolver = queryParameterBindingResolver;
|
this.queryParameterBindingResolver = queryParameterBindingResolver;
|
||||||
|
|
||||||
if ( valueMapping instanceof EntityValuedModelPart ) {
|
if ( valueMapping instanceof EntityAssociationMapping ) {
|
||||||
|
final EntityAssociationMapping mapping = (EntityAssociationMapping) valueMapping;
|
||||||
|
this.valueMapping = mapping.getForeignKeyDescriptor().getPart( mapping.getSideNature() );
|
||||||
|
}
|
||||||
|
else if ( valueMapping instanceof EntityValuedModelPart ) {
|
||||||
this.valueMapping = ( (EntityValuedModelPart) valueMapping ).getEntityMappingType().getIdentifierMapping();
|
this.valueMapping = ( (EntityValuedModelPart) valueMapping ).getEntityMappingType().getIdentifierMapping();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -153,7 +153,8 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
SqmPath<?> resolvedPath = null;
|
SqmPath<?> resolvedPath = null;
|
||||||
ModelPartContainer modelPartContainer = null;
|
ModelPartContainer modelPartContainer = null;
|
||||||
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
|
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
|
||||||
if ( sqmJoin instanceof SqmAttributeJoin<?, ?>
|
// We can only match singular joins here, as plural path parts are interpreted like sub-queries
|
||||||
|
if ( sqmJoin instanceof SqmSingularJoin<?, ?>
|
||||||
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
||||||
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
||||||
if ( attributeJoin.getOn() == null ) {
|
if ( attributeJoin.getOn() == null ) {
|
||||||
|
|
|
@ -26,8 +26,9 @@ public class SqmCorrelatedBagJoin<O, T> extends SqmBagJoin<O, T> implements SqmC
|
||||||
public SqmCorrelatedBagJoin(SqmBagJoin<O, T> correlationParent) {
|
public SqmCorrelatedBagJoin(SqmBagJoin<O, T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
correlationParent.getLhs(),
|
correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getAttribute(),
|
correlationParent.getAttribute(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
false,
|
false,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
|
|
|
@ -23,8 +23,9 @@ public class SqmCorrelatedCrossJoin<T> extends SqmCrossJoin<T> implements SqmCor
|
||||||
|
|
||||||
public SqmCorrelatedCrossJoin(SqmCrossJoin<T> correlationParent) {
|
public SqmCorrelatedCrossJoin(SqmCrossJoin<T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getReferencedPathSource(),
|
correlationParent.getReferencedPathSource(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
correlationParent.getRoot()
|
correlationParent.getRoot()
|
||||||
);
|
);
|
||||||
this.correlatedRootJoin = SqmCorrelatedRootJoin.create( correlationParent, this );
|
this.correlatedRootJoin = SqmCorrelatedRootJoin.create( correlationParent, this );
|
||||||
|
|
|
@ -24,8 +24,9 @@ public class SqmCorrelatedEntityJoin<T> extends SqmEntityJoin<T> implements SqmC
|
||||||
|
|
||||||
public SqmCorrelatedEntityJoin(SqmEntityJoin<T> correlationParent) {
|
public SqmCorrelatedEntityJoin(SqmEntityJoin<T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getReferencedPathSource(),
|
correlationParent.getReferencedPathSource(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
correlationParent.getRoot()
|
correlationParent.getRoot()
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,8 +26,9 @@ public class SqmCorrelatedListJoin<O, T> extends SqmListJoin<O, T> implements Sq
|
||||||
public SqmCorrelatedListJoin(SqmListJoin<O, T> correlationParent) {
|
public SqmCorrelatedListJoin(SqmListJoin<O, T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
correlationParent.getLhs(),
|
correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getAttribute(),
|
correlationParent.getAttribute(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
false,
|
false,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
|
|
|
@ -26,8 +26,9 @@ public class SqmCorrelatedMapJoin<O, K, V> extends SqmMapJoin<O, K, V> implement
|
||||||
public SqmCorrelatedMapJoin(SqmMapJoin<O, K, V> correlationParent) {
|
public SqmCorrelatedMapJoin(SqmMapJoin<O, K, V> correlationParent) {
|
||||||
super(
|
super(
|
||||||
correlationParent.getLhs(),
|
correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getAttribute(),
|
correlationParent.getAttribute(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
false,
|
false,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
|
|
|
@ -22,8 +22,9 @@ public class SqmCorrelatedPluralPartJoin<O, T> extends SqmPluralPartJoin<O, T> i
|
||||||
public SqmCorrelatedPluralPartJoin(SqmPluralPartJoin<O, T> correlationParent) {
|
public SqmCorrelatedPluralPartJoin(SqmPluralPartJoin<O, T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
(SqmFrom<?, O>) correlationParent.getLhs(),
|
(SqmFrom<?, O>) correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getReferencedPathSource(),
|
correlationParent.getReferencedPathSource(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class SqmCorrelatedRoot<T> extends SqmRoot<T> implements SqmPathWrapper<T
|
||||||
super(
|
super(
|
||||||
correlationParent.getNavigablePath(),
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getReferencedPathSource(),
|
correlationParent.getReferencedPathSource(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
);
|
);
|
||||||
this.correlationParent = correlationParent;
|
this.correlationParent = correlationParent;
|
||||||
|
|
|
@ -26,8 +26,9 @@ public class SqmCorrelatedSetJoin<O, T> extends SqmSetJoin<O, T> implements SqmC
|
||||||
public SqmCorrelatedSetJoin(SqmSetJoin<O, T> correlationParent) {
|
public SqmCorrelatedSetJoin(SqmSetJoin<O, T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
correlationParent.getLhs(),
|
correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getAttribute(),
|
correlationParent.getAttribute(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
false,
|
false,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
|
|
|
@ -26,8 +26,9 @@ public class SqmCorrelatedSingularJoin<O, T> extends SqmSingularJoin<O, T> imple
|
||||||
public SqmCorrelatedSingularJoin(SqmSingularJoin<O, T> correlationParent) {
|
public SqmCorrelatedSingularJoin(SqmSingularJoin<O, T> correlationParent) {
|
||||||
super(
|
super(
|
||||||
correlationParent.getLhs(),
|
correlationParent.getLhs(),
|
||||||
|
correlationParent.getNavigablePath(),
|
||||||
correlationParent.getAttribute(),
|
correlationParent.getAttribute(),
|
||||||
null,
|
correlationParent.getExplicitAlias(),
|
||||||
SqmJoinType.INNER,
|
SqmJoinType.INNER,
|
||||||
false,
|
false,
|
||||||
correlationParent.nodeBuilder()
|
correlationParent.nodeBuilder()
|
||||||
|
|
|
@ -24,51 +24,49 @@ import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.QueryException;
|
import org.hibernate.QueryException;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.RowLockStrategy;
|
import org.hibernate.dialect.RowLockStrategy;
|
||||||
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
||||||
import org.hibernate.internal.util.MathHelper;
|
|
||||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.persister.entity.Queryable;
|
|
||||||
import org.hibernate.persister.internal.SqlFragmentPredicate;
|
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
|
||||||
import org.hibernate.query.sqm.FrameExclusion;
|
|
||||||
import org.hibernate.query.sqm.FrameKind;
|
|
||||||
import org.hibernate.query.sqm.FrameMode;
|
|
||||||
import org.hibernate.query.sqm.SetOperator;
|
|
||||||
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
|
||||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
|
||||||
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.AbstractDelegatingWrapperOptions;
|
import org.hibernate.engine.spi.AbstractDelegatingWrapperOptions;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.internal.FilterJdbcParameter;
|
import org.hibernate.internal.FilterJdbcParameter;
|
||||||
|
import org.hibernate.internal.util.MathHelper;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
|
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.SqlExpressible;
|
import org.hibernate.metamodel.mapping.SqlExpressible;
|
||||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.persister.entity.Queryable;
|
||||||
|
import org.hibernate.persister.internal.SqlFragmentPredicate;
|
||||||
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
import org.hibernate.query.spi.Limit;
|
import org.hibernate.query.spi.Limit;
|
||||||
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
|
import org.hibernate.query.sqm.FrameExclusion;
|
||||||
|
import org.hibernate.query.sqm.FrameKind;
|
||||||
|
import org.hibernate.query.sqm.FrameMode;
|
||||||
import org.hibernate.query.sqm.NullPrecedence;
|
import org.hibernate.query.sqm.NullPrecedence;
|
||||||
|
import org.hibernate.query.sqm.SetOperator;
|
||||||
import org.hibernate.query.sqm.SortOrder;
|
import org.hibernate.query.sqm.SortOrder;
|
||||||
import org.hibernate.query.sqm.UnaryArithmeticOperator;
|
import org.hibernate.query.sqm.UnaryArithmeticOperator;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
|
||||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
|
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||||
import org.hibernate.query.sqm.tree.expression.Conversion;
|
import org.hibernate.query.sqm.tree.expression.Conversion;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
@ -80,6 +78,8 @@ import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.cte.SearchClauseSpecification;
|
import org.hibernate.sql.ast.tree.cte.SearchClauseSpecification;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
@ -3608,9 +3608,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
clauseStack.push( Clause.FROM );
|
clauseStack.push( Clause.FROM );
|
||||||
String separator = NO_SEPARATOR;
|
String separator = NO_SEPARATOR;
|
||||||
for ( TableGroup root : fromClause.getRoots() ) {
|
for ( TableGroup root : fromClause.getRoots() ) {
|
||||||
appendSql( separator );
|
// Skip virtual table group roots which we use for simple correlations
|
||||||
renderRootTableGroup( root, null );
|
if ( !( root instanceof VirtualTableGroup ) ) {
|
||||||
separator = COMA_SEPARATOR;
|
appendSql( separator );
|
||||||
|
renderRootTableGroup( root, null );
|
||||||
|
separator = COMA_SEPARATOR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|
|
@ -20,7 +20,12 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
*/
|
*/
|
||||||
public interface FromClauseAccess {
|
public interface FromClauseAccess {
|
||||||
|
|
||||||
TableGroup findTableGroupOnLeaf(NavigablePath navigablePath);
|
/**
|
||||||
|
* Find a TableGroup by the NavigablePath it is registered under,
|
||||||
|
* and if not found on the current from clause level, ask the parent. Returns
|
||||||
|
* {@code null} if no TableGroup is registered under that NavigablePath
|
||||||
|
*/
|
||||||
|
TableGroup findTableGroupOnParents(NavigablePath navigablePath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a TableGroup by the NavigablePath it is registered under. Returns
|
* Find a TableGroup by the NavigablePath it is registered under. Returns
|
||||||
|
|
|
@ -32,17 +32,17 @@ public class SimpleFromClauseAccessImpl implements FromClauseAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup findTableGroupOnLeaf(NavigablePath navigablePath) {
|
public TableGroup findTableGroupOnParents(NavigablePath navigablePath) {
|
||||||
return tableGroupMap.get( navigablePath );
|
final TableGroup tableGroup = tableGroupMap.get( navigablePath );
|
||||||
|
if ( tableGroup == null && parent != null ) {
|
||||||
|
return parent.findTableGroupOnParents( navigablePath );
|
||||||
|
}
|
||||||
|
return tableGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup findTableGroup(NavigablePath navigablePath) {
|
public TableGroup findTableGroup(NavigablePath navigablePath) {
|
||||||
final TableGroup tableGroup = tableGroupMap.get( navigablePath );
|
return tableGroupMap.get( navigablePath );
|
||||||
if ( tableGroup == null && parent != null ) {
|
|
||||||
return parent.findTableGroup( navigablePath );
|
|
||||||
}
|
|
||||||
return tableGroup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.hibernate.query.spi.NavigablePath;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup {
|
public class MutatingTableReferenceGroupWrapper implements TableGroup {
|
||||||
private final NavigablePath navigablePath;
|
private final NavigablePath navigablePath;
|
||||||
private final ModelPartContainer modelPart;
|
private final ModelPartContainer modelPart;
|
||||||
private final NamedTableReference mutatingTableReference;
|
private final NamedTableReference mutatingTableReference;
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.hibernate.query.spi.NavigablePath;
|
||||||
/**
|
/**
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
*/
|
*/
|
||||||
public class UnionTableGroup extends AbstractTableGroup implements VirtualTableGroup {
|
public class UnionTableGroup extends AbstractTableGroup {
|
||||||
private final UnionTableReference tableReference;
|
private final UnionTableReference tableReference;
|
||||||
|
|
||||||
public UnionTableGroup(
|
public UnionTableGroup(
|
||||||
|
|
|
@ -869,4 +869,12 @@ public class IndexedCollectionTest {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryWithKeywordAsFromAlias(SessionFactoryScope scope) {
|
||||||
|
// This test would fail if we didn't use the proper parsing rule for the FROM alias
|
||||||
|
scope.inSession(
|
||||||
|
s -> s.createQuery( "from Version version" ).getResultList()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class SubqueryInSelectClauseTest extends AbstractSubqueryInSelectClauseTe
|
||||||
|
|
||||||
Subquery<Long> personCount = query.subquery( Long.class );
|
Subquery<Long> personCount = query.subquery( Long.class );
|
||||||
Root<Person> person = personCount.from( Person.class );
|
Root<Person> person = personCount.from( Person.class );
|
||||||
personCount.select( cb.count( person ) ).where( cb.equal( contacts.get( "id" ), person.get( "id" ) ) );
|
personCount.select( cb.count( person ) ).where( cb.equal( personCount.correlate( contacts ).get( "id" ), person.get( "id" ) ) );
|
||||||
|
|
||||||
query.multiselect( document.get( "id" ), personCount.getSelection() );
|
query.multiselect( document.get( "id" ), personCount.getSelection() );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* 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.criteria.valuehandlingmode.inline;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.Jpa;
|
||||||
|
import org.hibernate.testing.orm.junit.Setting;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@Jpa(
|
||||||
|
annotatedClasses = {
|
||||||
|
NonPkAssociationEqualityPredicateParameterTest.Customer.class,
|
||||||
|
NonPkAssociationEqualityPredicateParameterTest.Order.class
|
||||||
|
}
|
||||||
|
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "bind")
|
||||||
|
)
|
||||||
|
public class NonPkAssociationEqualityPredicateParameterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualityCheck(EntityManagerFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Order> orderCriteria = builder.createQuery( Order.class );
|
||||||
|
Root<Order> orderRoot = orderCriteria.from( Order.class );
|
||||||
|
|
||||||
|
orderCriteria.select( orderRoot );
|
||||||
|
Customer c = new Customer();
|
||||||
|
c.customerNumber = 123L;
|
||||||
|
orderCriteria.where(
|
||||||
|
builder.equal( orderRoot.get( "customer" ), c )
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Order> orders = entityManager.createQuery( orderCriteria ).getResultList();
|
||||||
|
assertTrue( orders.size() == 0 );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "ORDER_TABLE")
|
||||||
|
public static class Order {
|
||||||
|
private String id;
|
||||||
|
private double totalPrice;
|
||||||
|
private Customer customer;
|
||||||
|
|
||||||
|
public Order() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(String id, double totalPrice) {
|
||||||
|
this.id = id;
|
||||||
|
this.totalPrice = totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(String id, Customer customer) {
|
||||||
|
this.id = id;
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID")
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "TOTALPRICE")
|
||||||
|
public double getTotalPrice() {
|
||||||
|
return totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalPrice(double price) {
|
||||||
|
this.totalPrice = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "FK4_FOR_CUSTOMER_TABLE", referencedColumnName = "CUSTOMER_NUMBER")
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "CUSTOMER_TABLE")
|
||||||
|
public static class Customer {
|
||||||
|
private String id;
|
||||||
|
private Long customerNumber;
|
||||||
|
private String name;
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
public Customer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer(String id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by test case for HHH-8699.
|
||||||
|
public Customer(String id, String name, String greeting, Boolean something) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID")
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String v) {
|
||||||
|
this.id = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "CUSTOMER_NUMBER", unique = true)
|
||||||
|
public Long getCustomerNumber() {
|
||||||
|
return customerNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerNumber(Long customerNumber) {
|
||||||
|
this.customerNumber = customerNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "NAME")
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String v) {
|
||||||
|
this.name = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "AGE")
|
||||||
|
public Integer getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAge(Integer age) {
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,8 +60,17 @@ public class NonPkAssociationEqualityPredicateTest {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@Test
|
||||||
|
public void testDifferentAssociationsEqualityCheck(EntityManagerFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
// This fails because we compare a ToOne with non-PK to something with a EntityValuedModelPart which defaults to the PK mapping
|
||||||
|
entityManager.createQuery( "from Order o, Customer c where o.customer = c" ).getResultList();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity(name = "Order")
|
||||||
@Table(name = "ORDER_TABLE")
|
@Table(name = "ORDER_TABLE")
|
||||||
public static class Order {
|
public static class Order {
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -115,7 +124,7 @@ public class NonPkAssociationEqualityPredicateTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity(name = "Customer")
|
||||||
@Table(name = "CUSTOMER_TABLE")
|
@Table(name = "CUSTOMER_TABLE")
|
||||||
public static class Customer {
|
public static class Customer {
|
||||||
private String id;
|
private String id;
|
||||||
|
|
|
@ -52,6 +52,15 @@ public class IsEmptyPredicateTest {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptinessPredicatesWithJoin(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final List<Integer> ids = session.createQuery( "select p.id from Person p left join p.nicknames n where p.nicknames is not empty", Integer.class ).list();
|
||||||
|
assertThat( ids ).contains( personaWithSingleNicknameId, personWithMultipleNicknamesId );
|
||||||
|
assertThat( ids ).doesNotContain( personWithoutNicknameId );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
protected void prepareTestData(SessionFactoryScope scope) {
|
protected void prepareTestData(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
|
Loading…
Reference in New Issue