@@ -3530,10 +3530,14 @@ private TableGroup getPluralPartTableGroup(PluralTableGroup pluralTableGroup, Sq
35303530 }
35313531
35323532 private <X > X prepareReusablePath (SqmPath <?> sqmPath , Supplier <X > supplier ) {
3533- return prepareReusablePath ( sqmPath , fromClauseIndexStack .getCurrent (), supplier );
3533+ return prepareReusablePath ( sqmPath , fromClauseIndexStack .getCurrent (), supplier , false );
35343534 }
35353535
3536- private <X > X prepareReusablePath (SqmPath <?> sqmPath , FromClauseIndex fromClauseIndex , Supplier <X > supplier ) {
3536+ private <X > X prepareReusablePath (
3537+ SqmPath <?> sqmPath ,
3538+ FromClauseIndex fromClauseIndex ,
3539+ Supplier <X > supplier ,
3540+ boolean allowLeftJoins ) {
35373541 final Consumer <TableGroup > implicitJoinChecker ;
35383542 if ( getCurrentClauseStack ().getCurrent () != Clause .SET_EXPRESSION ) {
35393543 implicitJoinChecker = tg -> {};
@@ -3556,7 +3560,8 @@ private <X> X prepareReusablePath(SqmPath<?> sqmPath, FromClauseIndex fromClause
35563560 fromClauseIndex .getTableGroup ( sqmPath .getLhs ().getNavigablePath () ),
35573561 sqmPath
35583562 ),
3559- sqmPath
3563+ sqmPath ,
3564+ allowLeftJoins
35603565 );
35613566 if ( createdTableGroup != null ) {
35623567 if ( sqmPath instanceof SqmTreatedPath <?, ?> ) {
@@ -3610,7 +3615,7 @@ else if ( createdParentTableGroup instanceof PluralTableGroup ) {
36103615 }
36113616 else {
36123617 newTableGroup = getActualTableGroup (
3613- createTableGroup ( createdParentTableGroup , parentPath ),
3618+ createTableGroup ( createdParentTableGroup , parentPath , false ),
36143619 sqmPath
36153620 );
36163621 }
@@ -3624,9 +3629,21 @@ else if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
36243629 fromClauseIndex .register ( sqmPath , parentTableGroup );
36253630 }
36263631
3627- if ( parentPath instanceof SqmSimplePath <?>
3632+ upgradeToInnerJoinIfNeeded ( parentTableGroup , sqmPath , parentPath , fromClauseIndex );
3633+
3634+ registerPathAttributeEntityNameUsage ( sqmPath , parentTableGroup );
3635+
3636+ return parentTableGroup ;
3637+ }
3638+
3639+ private void upgradeToInnerJoinIfNeeded (
3640+ TableGroup parentTableGroup ,
3641+ SqmPath <?> sqmPath ,
3642+ SqmPath <?> parentPath ,
3643+ FromClauseIndex fromClauseIndex ) {
3644+ if ( getCurrentClauseStack ().getCurrent () != Clause .SELECT
3645+ && parentPath instanceof SqmSimplePath <?>
36283646 && CollectionPart .Nature .fromName ( parentPath .getNavigablePath ().getLocalName () ) == null
3629- && getCurrentClauseStack ().getCurrent () != Clause .SELECT
36303647 && parentPath .getParentPath () != null
36313648 && parentTableGroup .getModelPart () instanceof ToOneAttributeMapping ) {
36323649 // we need to handle the case of an implicit path involving a to-one
@@ -3650,9 +3667,6 @@ && getCurrentClauseStack().getCurrent() != Clause.SELECT
36503667 }
36513668 }
36523669 }
3653- registerPathAttributeEntityNameUsage ( sqmPath , parentTableGroup );
3654-
3655- return parentTableGroup ;
36563670 }
36573671
36583672 private void prepareForSelection (SqmPath <?> selectionPath ) {
@@ -3678,7 +3692,8 @@ private void prepareForSelection(SqmPath<?> selectionPath) {
36783692 // But only create it for paths that are not handled by #prepareReusablePath anyway
36793693 final TableGroup createdTableGroup = createTableGroup (
36803694 getActualTableGroup ( fromClauseIndex .getTableGroup ( path .getLhs ().getNavigablePath () ), path ),
3681- path
3695+ path ,
3696+ false
36823697 );
36833698 if ( createdTableGroup != null ) {
36843699 registerEntityNameProjectionUsage ( path , createdTableGroup );
@@ -3699,7 +3714,7 @@ private void prepareForSelection(SqmPath<?> selectionPath) {
36993714 }
37003715 }
37013716
3702- private TableGroup createTableGroup (TableGroup parentTableGroup , SqmPath <?> joinedPath ) {
3717+ private TableGroup createTableGroup (TableGroup parentTableGroup , SqmPath <?> joinedPath , boolean allowLeftJoins ) {
37033718 final SqmPath <?> lhsPath = joinedPath .getLhs ();
37043719 final FromClauseIndex fromClauseIndex = getFromClauseIndex ();
37053720 final ModelPart subPart = parentTableGroup .getModelPart ().findSubPart (
@@ -3731,18 +3746,30 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
37313746 querySpec .getFromClause ().addRoot ( tableGroup );
37323747 }
37333748 else {
3734- // Check if we can reuse a table group join of the parent
3735- final TableGroup compatibleTableGroup = parentTableGroup .findCompatibleJoinedGroup (
3736- joinProducer ,
3737- SqlAstJoinType .INNER
3738- );
3749+ final TableGroupJoin compatibleLeftJoin ;
3750+ final SqlAstJoinType sqlAstJoinType ;
3751+ if ( isMappedByOrNotFoundToOne ( joinProducer ) ) {
3752+ compatibleLeftJoin = parentTableGroup .findCompatibleJoin (
3753+ joinProducer ,
3754+ SqlAstJoinType .LEFT
3755+ );
3756+ sqlAstJoinType = SqlAstJoinType .LEFT ;
3757+ }
3758+ else {
3759+ compatibleLeftJoin = null ;
3760+ sqlAstJoinType = null ;
3761+ }
3762+
3763+ final TableGroup compatibleTableGroup = compatibleLeftJoin != null ?
3764+ compatibleLeftJoin .getJoinedGroup () :
3765+ parentTableGroup .findCompatibleJoinedGroup ( joinProducer , SqlAstJoinType .INNER );
37393766 if ( compatibleTableGroup == null ) {
37403767 final TableGroupJoin tableGroupJoin = joinProducer .createTableGroupJoin (
37413768 joinedPath .getNavigablePath (),
37423769 parentTableGroup ,
37433770 null ,
37443771 null ,
3745- null ,
3772+ allowLeftJoins ? sqlAstJoinType : null ,
37463773 false ,
37473774 false ,
37483775 this
@@ -3762,6 +3789,10 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
37623789 // Also register the table group under its original navigable path, which possibly contains an alias
37633790 // This is important, as otherwise we might create new joins in subqueries which are unnecessary
37643791 fromClauseIndex .registerTableGroup ( tableGroup .getNavigablePath (), tableGroup );
3792+ // Upgrade the join type to inner if the context doesn't allow left joins
3793+ if ( compatibleLeftJoin != null && !allowLeftJoins ) {
3794+ compatibleLeftJoin .setJoinType ( SqlAstJoinType .INNER );
3795+ }
37653796 }
37663797 }
37673798
@@ -3774,6 +3805,16 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
37743805 return tableGroup ;
37753806 }
37763807
3808+ private boolean isMappedByOrNotFoundToOne (TableGroupJoinProducer joinProducer ) {
3809+ if ( joinProducer instanceof ToOneAttributeMapping ) {
3810+ final ToOneAttributeMapping toOne = (ToOneAttributeMapping ) joinProducer ;
3811+ if ( toOne .hasNotFoundAction () || toOne .getReferencedPropertyName () != null ) {
3812+ return true ;
3813+ }
3814+ }
3815+ return false ;
3816+ }
3817+
37773818 private boolean isRecursiveCte (TableGroup tableGroup ) {
37783819 if ( tableGroup instanceof CteTableGroup ) {
37793820 final CteTableGroup cteTableGroup = (CteTableGroup ) tableGroup ;
@@ -7530,11 +7571,30 @@ public LikePredicate visitLikePredicate(SqmLikePredicate predicate) {
75307571
75317572 @ Override
75327573 public NullnessPredicate visitIsNullPredicate (SqmNullnessPredicate predicate ) {
7533- return new NullnessPredicate (
7534- (Expression ) visitWithInferredType ( predicate .getExpression (), () -> basicType ( Object .class )),
7535- predicate .isNegated (),
7536- getBooleanType ()
7537- );
7574+ final SqmExpression <?> sqmExpression = predicate .getExpression ();
7575+ final Expression expression ;
7576+ if ( sqmExpression instanceof SqmEntityValuedSimplePath <?> ) {
7577+ final SqmEntityValuedSimplePath <?> entityValuedPath = (SqmEntityValuedSimplePath <?>) sqmExpression ;
7578+ inferrableTypeAccessStack .push ( () -> basicType ( Object .class ) );
7579+ expression = withTreatRestriction ( prepareReusablePath (
7580+ entityValuedPath ,
7581+ fromClauseIndexStack .getCurrent (),
7582+ () -> EntityValuedPathInterpretation .from (
7583+ entityValuedPath ,
7584+ getInferredValueMapping (),
7585+ this
7586+ ),
7587+ true
7588+ ), entityValuedPath );
7589+ inferrableTypeAccessStack .pop ();
7590+ }
7591+ else {
7592+ expression = (Expression ) visitWithInferredType (
7593+ predicate .getExpression (),
7594+ () -> basicType ( Object .class )
7595+ );
7596+ }
7597+ return new NullnessPredicate ( expression , predicate .isNegated (), getBooleanType () );
75387598 }
75397599
75407600 @ Override
0 commit comments