Skip to content

Commit 32a0227

Browse files
authored
Do not move ON condition to Where clause in LINQ (#2539)
1 parent ee0d520 commit 32a0227

File tree

4 files changed

+21
-78
lines changed

4 files changed

+21
-78
lines changed

src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,51 +1078,40 @@ from c in db.Customers
10781078
}
10791079

10801080
[Category("JOIN")]
1081-
[TestCase(true, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1082-
[TestCase(false, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1083-
public async Task DLinqJoin5dAsync(bool useCrossJoin)
1081+
[Test(Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1082+
public async Task DLinqJoin5dAsync()
10841083
{
1085-
if (useCrossJoin && !Dialect.SupportsCrossJoin)
1086-
{
1087-
Assert.Ignore("Dialect does not support cross join.");
1088-
}
1089-
10901084
var q =
10911085
from c in db.Customers
10921086
join o in db.Orders on
10931087
new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals
10941088
new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null }
10951089
select new { c.ContactName, o.OrderId };
10961090

1097-
using (var substitute = SubstituteDialect())
10981091
using (var sqlSpy = new SqlLogSpy())
10991092
{
1100-
ClearQueryPlanCache();
1101-
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);
1102-
11031093
await (ObjectDumper.WriteAsync(q));
11041094

11051095
var sql = sqlSpy.GetWholeLog();
1106-
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
11071096
Assert.That(GetTotalOccurrences(sql, "left outer join"), Is.EqualTo(0));
1108-
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(useCrossJoin ? 1 : 2));
1109-
Assert.That(GetTotalOccurrences(sql, "cross join"), Is.EqualTo(useCrossJoin ? 1 : 0));
1097+
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(2));
1098+
Assert.That(GetTotalOccurrences(sql, "cross join"), Is.EqualTo(0));
11101099
}
11111100
}
11121101

11131102
[Category("JOIN")]
11141103
[Test(Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1115-
public void DLinqJoin5dLeftJoinAsync()
1104+
public async Task DLinqJoin5dLeftJoinAsync()
11161105
{
11171106
var q =
11181107
from c in db.Customers
11191108
join o in db.Orders on
1120-
new { c.CustomerId, HasContractTitle = c.ContactTitle != null } equals
1121-
new { o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null } into orders
1109+
new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals
1110+
new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null} into orders
11221111
from o in orders.DefaultIfEmpty()
1123-
select new { c.ContactName, o.OrderId };
1112+
select new {c.ContactName, OrderId = (int?) o.OrderId};
11241113

1125-
Assert.ThrowsAsync<NotSupportedException>(() => ObjectDumper.WriteAsync(q));
1114+
await (ObjectDumper.WriteAsync(q));
11261115
}
11271116

11281117
[Category("JOIN")]

src/NHibernate.Test/Linq/LinqQuerySamples.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,35 +1622,24 @@ from c in db.Customers
16221622
}
16231623

16241624
[Category("JOIN")]
1625-
[TestCase(true, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1626-
[TestCase(false, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1627-
public void DLinqJoin5d(bool useCrossJoin)
1625+
[Test(Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1626+
public void DLinqJoin5d()
16281627
{
1629-
if (useCrossJoin && !Dialect.SupportsCrossJoin)
1630-
{
1631-
Assert.Ignore("Dialect does not support cross join.");
1632-
}
1633-
16341628
var q =
16351629
from c in db.Customers
16361630
join o in db.Orders on
16371631
new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals
16381632
new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null }
16391633
select new { c.ContactName, o.OrderId };
16401634

1641-
using (var substitute = SubstituteDialect())
16421635
using (var sqlSpy = new SqlLogSpy())
16431636
{
1644-
ClearQueryPlanCache();
1645-
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);
1646-
16471637
ObjectDumper.Write(q);
16481638

16491639
var sql = sqlSpy.GetWholeLog();
1650-
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
16511640
Assert.That(GetTotalOccurrences(sql, "left outer join"), Is.EqualTo(0));
1652-
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(useCrossJoin ? 1 : 2));
1653-
Assert.That(GetTotalOccurrences(sql, "cross join"), Is.EqualTo(useCrossJoin ? 1 : 0));
1641+
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(2));
1642+
Assert.That(GetTotalOccurrences(sql, "cross join"), Is.EqualTo(0));
16541643
}
16551644
}
16561645

@@ -1661,12 +1650,12 @@ public void DLinqJoin5dLeftJoin()
16611650
var q =
16621651
from c in db.Customers
16631652
join o in db.Orders on
1664-
new { c.CustomerId, HasContractTitle = c.ContactTitle != null } equals
1665-
new { o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null } into orders
1653+
new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals
1654+
new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null} into orders
16661655
from o in orders.DefaultIfEmpty()
1667-
select new { c.ContactName, o.OrderId };
1656+
select new {c.ContactName, OrderId = (int?) o.OrderId};
16681657

1669-
Assert.Throws<NotSupportedException>(() => ObjectDumper.Write(q));
1658+
ObjectDumper.Write(q);
16701659
}
16711660

16721661
[Category("JOIN")]

src/NHibernate/Linq/ReWriters/AddJoinsReWriter.cs

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Collections.Specialized;
42
using System.Linq;
53
using System.Linq.Expressions;
64
using NHibernate.Engine;
@@ -22,13 +20,11 @@ public class AddJoinsReWriter : NhQueryModelVisitorBase, IIsEntityDecider, INhQu
2220
private readonly ISessionFactoryImplementor _sessionFactory;
2321
private readonly MemberExpressionJoinDetector _memberExpressionJoinDetector;
2422
private readonly WhereJoinDetector _whereJoinDetector;
25-
private JoinClause _currentJoin;
26-
private bool? _innerJoin;
2723

2824
private AddJoinsReWriter(ISessionFactoryImplementor sessionFactory, QueryModel queryModel)
2925
{
3026
_sessionFactory = sessionFactory;
31-
var joiner = new Joiner(queryModel, AddJoin);
27+
var joiner = new Joiner(queryModel);
3228
_memberExpressionJoinDetector = new MemberExpressionJoinDetector(this, joiner, _sessionFactory);
3329
_whereJoinDetector = new WhereJoinDetector(this, joiner, _sessionFactory);
3430
}
@@ -66,30 +62,17 @@ public override void VisitNhHavingClause(NhHavingClause havingClause, QueryModel
6662

6763
public void VisitNhOuterJoinClause(NhOuterJoinClause nhOuterJoinClause, QueryModel queryModel, int index)
6864
{
69-
VisitJoinClause(nhOuterJoinClause.JoinClause, false);
65+
VisitJoinClause(nhOuterJoinClause.JoinClause);
7066
}
7167

7268
public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index)
7369
{
74-
VisitJoinClause(joinClause, true);
70+
VisitJoinClause(joinClause);
7571
}
7672

77-
private void VisitJoinClause(JoinClause joinClause, bool innerJoin)
73+
private void VisitJoinClause(JoinClause joinClause)
7874
{
7975
joinClause.InnerSequence = _whereJoinDetector.Transform(joinClause.InnerSequence);
80-
81-
// When associations are located in the outer key (e.g. from a in A join b in B b on a.C.D.Id equals b.Id),
82-
// do nothing and leave them to HQL for adding the missing joins.
83-
84-
// When associations are located in the inner key (e.g. from a in A join b in B b on a.Id equals b.C.D.Id),
85-
// we have to move the condition to the where statement, otherwise the query will be invalid (HQL does not
86-
// support them).
87-
// Link newly created joins with the current join clause in order to later detect which join type to use.
88-
_currentJoin = joinClause;
89-
_innerJoin = innerJoin;
90-
joinClause.InnerKeySelector = _whereJoinDetector.Transform(joinClause.InnerKeySelector);
91-
_currentJoin = null;
92-
_innerJoin = null;
9376
}
9477

9578
// Since v5.3
@@ -107,18 +90,6 @@ public bool IsIdentifier(System.Type type, string propertyName)
10790
return metadata != null && propertyName.Equals(metadata.IdentifierPropertyName);
10891
}
10992

110-
private void AddJoin(QueryModel queryModel, NhJoinClause joinClause)
111-
{
112-
joinClause.ParentJoinClause = _currentJoin;
113-
if (_innerJoin == true)
114-
{
115-
// Match the parent join type
116-
joinClause.MakeInner();
117-
}
118-
119-
queryModel.BodyClauses.Add(joinClause);
120-
}
121-
12293
bool IIsEntityDecider.IsEntity(MemberExpression expression, out bool isIdentifier)
12394
{
12495
isIdentifier =

src/NHibernate/Linq/Visitors/JoinBuilder.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ public class Joiner : IJoiner
2121
private readonly NameGenerator _nameGenerator;
2222
private readonly QueryModel _queryModel;
2323

24-
internal Joiner(QueryModel queryModel, System.Action<QueryModel, NhJoinClause> addJoinMethod)
25-
: this(queryModel)
26-
{
27-
AddJoinMethod = addJoinMethod;
28-
}
29-
3024
internal Joiner(QueryModel queryModel)
3125
{
3226
_nameGenerator = new NameGenerator(queryModel);

0 commit comments

Comments
 (0)