@@ -3530,10 +3530,14 @@ private TableGroup getPluralPartTableGroup(PluralTableGroup pluralTableGroup, Sq
3530
3530
}
3531
3531
3532
3532
private <X > X prepareReusablePath (SqmPath <?> sqmPath , Supplier <X > supplier ) {
3533
- return prepareReusablePath ( sqmPath , fromClauseIndexStack .getCurrent (), supplier );
3533
+ return prepareReusablePath ( sqmPath , fromClauseIndexStack .getCurrent (), supplier , false );
3534
3534
}
3535
3535
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 ) {
3537
3541
final Consumer <TableGroup > implicitJoinChecker ;
3538
3542
if ( getCurrentClauseStack ().getCurrent () != Clause .SET_EXPRESSION ) {
3539
3543
implicitJoinChecker = tg -> {};
@@ -3556,7 +3560,8 @@ private <X> X prepareReusablePath(SqmPath<?> sqmPath, FromClauseIndex fromClause
3556
3560
fromClauseIndex .getTableGroup ( sqmPath .getLhs ().getNavigablePath () ),
3557
3561
sqmPath
3558
3562
),
3559
- sqmPath
3563
+ sqmPath ,
3564
+ allowLeftJoins
3560
3565
);
3561
3566
if ( createdTableGroup != null ) {
3562
3567
if ( sqmPath instanceof SqmTreatedPath <?, ?> ) {
@@ -3610,7 +3615,7 @@ else if ( createdParentTableGroup instanceof PluralTableGroup ) {
3610
3615
}
3611
3616
else {
3612
3617
newTableGroup = getActualTableGroup (
3613
- createTableGroup ( createdParentTableGroup , parentPath ),
3618
+ createTableGroup ( createdParentTableGroup , parentPath , false ),
3614
3619
sqmPath
3615
3620
);
3616
3621
}
@@ -3624,9 +3629,21 @@ else if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
3624
3629
fromClauseIndex .register ( sqmPath , parentTableGroup );
3625
3630
}
3626
3631
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 <?>
3628
3646
&& CollectionPart .Nature .fromName ( parentPath .getNavigablePath ().getLocalName () ) == null
3629
- && getCurrentClauseStack ().getCurrent () != Clause .SELECT
3630
3647
&& parentPath .getParentPath () != null
3631
3648
&& parentTableGroup .getModelPart () instanceof ToOneAttributeMapping ) {
3632
3649
// we need to handle the case of an implicit path involving a to-one
@@ -3650,9 +3667,6 @@ && getCurrentClauseStack().getCurrent() != Clause.SELECT
3650
3667
}
3651
3668
}
3652
3669
}
3653
- registerPathAttributeEntityNameUsage ( sqmPath , parentTableGroup );
3654
-
3655
- return parentTableGroup ;
3656
3670
}
3657
3671
3658
3672
private void prepareForSelection (SqmPath <?> selectionPath ) {
@@ -3678,7 +3692,8 @@ private void prepareForSelection(SqmPath<?> selectionPath) {
3678
3692
// But only create it for paths that are not handled by #prepareReusablePath anyway
3679
3693
final TableGroup createdTableGroup = createTableGroup (
3680
3694
getActualTableGroup ( fromClauseIndex .getTableGroup ( path .getLhs ().getNavigablePath () ), path ),
3681
- path
3695
+ path ,
3696
+ false
3682
3697
);
3683
3698
if ( createdTableGroup != null ) {
3684
3699
registerEntityNameProjectionUsage ( path , createdTableGroup );
@@ -3699,7 +3714,7 @@ private void prepareForSelection(SqmPath<?> selectionPath) {
3699
3714
}
3700
3715
}
3701
3716
3702
- private TableGroup createTableGroup (TableGroup parentTableGroup , SqmPath <?> joinedPath ) {
3717
+ private TableGroup createTableGroup (TableGroup parentTableGroup , SqmPath <?> joinedPath , boolean allowLeftJoins ) {
3703
3718
final SqmPath <?> lhsPath = joinedPath .getLhs ();
3704
3719
final FromClauseIndex fromClauseIndex = getFromClauseIndex ();
3705
3720
final ModelPart subPart = parentTableGroup .getModelPart ().findSubPart (
@@ -3731,18 +3746,30 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
3731
3746
querySpec .getFromClause ().addRoot ( tableGroup );
3732
3747
}
3733
3748
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 );
3739
3766
if ( compatibleTableGroup == null ) {
3740
3767
final TableGroupJoin tableGroupJoin = joinProducer .createTableGroupJoin (
3741
3768
joinedPath .getNavigablePath (),
3742
3769
parentTableGroup ,
3743
3770
null ,
3744
3771
null ,
3745
- null ,
3772
+ allowLeftJoins ? sqlAstJoinType : null ,
3746
3773
false ,
3747
3774
false ,
3748
3775
this
@@ -3762,6 +3789,10 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
3762
3789
// Also register the table group under its original navigable path, which possibly contains an alias
3763
3790
// This is important, as otherwise we might create new joins in subqueries which are unnecessary
3764
3791
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
+ }
3765
3796
}
3766
3797
}
3767
3798
@@ -3774,6 +3805,16 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
3774
3805
return tableGroup ;
3775
3806
}
3776
3807
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
+
3777
3818
private boolean isRecursiveCte (TableGroup tableGroup ) {
3778
3819
if ( tableGroup instanceof CteTableGroup ) {
3779
3820
final CteTableGroup cteTableGroup = (CteTableGroup ) tableGroup ;
@@ -7530,11 +7571,30 @@ public LikePredicate visitLikePredicate(SqmLikePredicate predicate) {
7530
7571
7531
7572
@ Override
7532
7573
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 () );
7538
7598
}
7539
7599
7540
7600
@ Override
0 commit comments