Skip to content

Commit 00af788

Browse files
bahusoidhazzik
andauthored
Handle both null and not null checks in queries on not-found='ignore' properties (#2989)
Co-authored-by: Alex Zaytsev <hazzik@gmail.com>
1 parent 2a98316 commit 00af788

File tree

4 files changed

+45
-11
lines changed

4 files changed

+45
-11
lines changed

src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs

+12
Original file line numberDiff line numberDiff line change
@@ -349,11 +349,23 @@ public async Task NullableEntityProjectionAsync()
349349
from x2 in session.Query<NullableOwner>()
350350
where x == x2 && x.ManyToOne == null && x.OneToOne.Name == null
351351
select x2).ToListAsync());
352+
353+
var validManyToOne = new OneToOneEntity{Name = "valid"};
354+
await (session.SaveAsync(validManyToOne));
355+
nullableOwner2.ManyToOne = validManyToOne;
356+
await (session.FlushAsync());
357+
358+
//GH-2988
359+
var withNullOrValidList = await (session.Query<NullableOwner>().Where(x => x.ManyToOne.Id == validManyToOne.Id || x.ManyToOne == null).ToListAsync());
360+
var withNullOrValidList2 = await (session.Query<NullableOwner>().Where(x => x.ManyToOne == null || x.ManyToOne.Id == validManyToOne.Id).ToListAsync());
361+
352362
Assert.That(fullList.Count, Is.EqualTo(2));
353363
Assert.That(withValidManyToOneList.Count, Is.EqualTo(0));
354364
Assert.That(withValidManyToOneList2.Count, Is.EqualTo(0));
355365
Assert.That(withNullManyToOneList.Count, Is.EqualTo(2));
356366
Assert.That(withNullManyToOneJoinedList.Count, Is.EqualTo(2));
367+
Assert.That(withNullOrValidList.Count, Is.EqualTo(2));
368+
Assert.That(withNullOrValidList2.Count, Is.EqualTo(2));
357369
}
358370
}
359371

src/NHibernate.Test/Hql/EntityJoinHqlTest.cs

+12
Original file line numberDiff line numberDiff line change
@@ -337,11 +337,23 @@ public void NullableEntityProjection()
337337
from x2 in session.Query<NullableOwner>()
338338
where x == x2 && x.ManyToOne == null && x.OneToOne.Name == null
339339
select x2).ToList();
340+
341+
var validManyToOne = new OneToOneEntity{Name = "valid"};
342+
session.Save(validManyToOne);
343+
nullableOwner2.ManyToOne = validManyToOne;
344+
session.Flush();
345+
346+
//GH-2988
347+
var withNullOrValidList = session.Query<NullableOwner>().Where(x => x.ManyToOne.Id == validManyToOne.Id || x.ManyToOne == null).ToList();
348+
var withNullOrValidList2 = session.Query<NullableOwner>().Where(x => x.ManyToOne == null || x.ManyToOne.Id == validManyToOne.Id).ToList();
349+
340350
Assert.That(fullList.Count, Is.EqualTo(2));
341351
Assert.That(withValidManyToOneList.Count, Is.EqualTo(0));
342352
Assert.That(withValidManyToOneList2.Count, Is.EqualTo(0));
343353
Assert.That(withNullManyToOneList.Count, Is.EqualTo(2));
344354
Assert.That(withNullManyToOneJoinedList.Count, Is.EqualTo(2));
355+
Assert.That(withNullOrValidList.Count, Is.EqualTo(2));
356+
Assert.That(withNullOrValidList2.Count, Is.EqualTo(2));
345357
}
346358
}
347359

src/NHibernate/Engine/JoinSequence.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private sealed class Join
4343
{
4444
private readonly IAssociationType associationType;
4545
private readonly IJoinable joinable;
46-
private readonly JoinType joinType;
46+
private JoinType joinType;
4747
private readonly string alias;
4848
private readonly string[] lhsColumns;
4949

@@ -75,6 +75,7 @@ public IJoinable Joinable
7575
public JoinType JoinType
7676
{
7777
get { return joinType; }
78+
internal set { joinType = value; }
7879
}
7980

8081
public string[] LHSColumns
@@ -329,5 +330,11 @@ public interface ISelector
329330
public ISessionFactoryImplementor Factory => factory;
330331

331332
internal bool ForceFilter { get; set; }
333+
334+
internal void SetJoinType(JoinType joinType)
335+
{
336+
joins[0].JoinType = joinType;
337+
SetUseThetaStyle(false);
338+
}
332339
}
333340
}

src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs

+13-10
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,8 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string
417417

418418
if ( joinIsNeeded )
419419
{
420-
if (comparisonWithNullableEntity && Walker.IsNullComparison)
421-
{
422-
implicitJoin = false;
423-
_joinType = JoinType.LeftOuterJoin;
424-
}
425-
426-
DereferenceEntityJoin( classAlias, entityType, implicitJoin, parent );
420+
var forceLeftJoin = comparisonWithNullableEntity && Walker.IsNullComparison;
421+
DereferenceEntityJoin(classAlias, entityType, implicitJoin, parent, forceLeftJoin);
427422
if (comparisonWithNullableEntity)
428423
{
429424
_columns = FromElement.GetIdentityColumns();
@@ -457,7 +452,7 @@ private void DereferenceEntityIdentifier(string propertyName, DotNode dotParent)
457452
}
458453
}
459454

460-
private void DereferenceEntityJoin(string classAlias, EntityType propertyType, bool impliedJoin, IASTNode parent)
455+
private void DereferenceEntityJoin(string classAlias, EntityType propertyType, bool impliedJoin, IASTNode parent, bool forceLeftJoin)
461456
{
462457
_dereferenceType = DerefEntity;
463458
if ( Log.IsDebugEnabled() )
@@ -476,7 +471,11 @@ private void DereferenceEntityJoin(string classAlias, EntityType propertyType, b
476471
string[] joinColumns = GetColumns();
477472
string joinPath = Path;
478473

479-
if ( impliedJoin && Walker.IsInFrom )
474+
if (forceLeftJoin)
475+
{
476+
_joinType = JoinType.LeftOuterJoin;
477+
}
478+
else if (impliedJoin && Walker.IsInFrom)
480479
{
481480
_joinType = Walker.ImpliedJoinType;
482481
}
@@ -524,7 +523,7 @@ private void DereferenceEntityJoin(string classAlias, EntityType propertyType, b
524523
// If this is an implied join in a from element, then use the impled join type which is part of the
525524
// tree parser's state (set by the gramamar actions).
526525
JoinSequence joinSequence = SessionFactoryHelper
527-
.CreateJoinSequence( impliedJoin, propertyType, tableAlias, _joinType, joinColumns );
526+
.CreateJoinSequence(!forceLeftJoin && impliedJoin, propertyType, tableAlias, _joinType, joinColumns);
528527

529528
var factory = new FromElementFactory(
530529
currentFromClause,
@@ -545,6 +544,10 @@ private void DereferenceEntityJoin(string classAlias, EntityType propertyType, b
545544
}
546545
else
547546
{
547+
if (forceLeftJoin)
548+
{
549+
elem.JoinSequence.SetJoinType(_joinType);
550+
}
548551
currentFromClause.AddDuplicateAlias(classAlias, elem);
549552
}
550553

0 commit comments

Comments
 (0)