Skip to content

Commit 93a3050

Browse files
Daniel Labergehazzik
Daniel Laberge
authored andcommitted
NH-3459 - Fix Left Outer Joins with DefaultIfEmpty() in conjunction with group by.
1 parent 1a5dc23 commit 93a3050

File tree

5 files changed

+302
-4
lines changed

5 files changed

+302
-4
lines changed

src/NHibernate.Test/NHSpecificTest/NH2379/Fixture.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,60 @@ from ol in o.OrderLines
108108
}
109109
}
110110

111+
[Test]
112+
public void InnerJoinWithRestriction()
113+
{
114+
//
115+
// select
116+
// order0_.Id as col_0_0_,
117+
// orderlines1_.Id as col_1_0_
118+
// from
119+
// Orders order0_
120+
// inner join
121+
// OrderLines orderlines1_
122+
// on order0_.Id=orderlines1_.OrderId
123+
// and orderlines1_.Name like ('Order Line 3')
124+
//
125+
126+
using (var session = OpenSession())
127+
using (session.BeginTransaction())
128+
{
129+
var result = (from o in session.Query<Order>()
130+
from ol in o.OrderLines.Where(x => x.Name.StartsWith("Order Line 3"))
131+
select new { OrderId = o.Id, OrderLineId = (Guid?)ol.Id }).ToList();
132+
133+
Assert.AreEqual(2, result.Count);
134+
}
135+
}
136+
137+
[Test]
138+
public void InnerJoinWithOutermostRestriction()
139+
{
140+
//
141+
// select
142+
// order0_.Id as col_0_0_,
143+
// orderlines1_.Id as col_1_0_
144+
// from
145+
// Orders order0_
146+
// inner join
147+
// OrderLines orderlines1_
148+
// on order0_.Id=orderlines1_.OrderId
149+
// where
150+
// orderlines1_.Name like ('Order Line 3')
151+
//
152+
153+
using (var session = OpenSession())
154+
using (session.BeginTransaction())
155+
{
156+
var result = (from o in session.Query<Order>()
157+
from ol in o.OrderLines
158+
where ol.Name.StartsWith("Order Line 3")
159+
select new { OrderId = o.Id, OrderLineId = (Guid?)ol.Id }).ToList();
160+
161+
Assert.AreEqual(2, result.Count);
162+
}
163+
}
164+
111165
[Test]
112166
public void LeftOuterJoin()
113167
{
@@ -162,6 +216,8 @@ from ol in o.OrderLines.Where(x => x.Name.StartsWith("Order Line 3")).DefaultIfE
162216
[Test]
163217
public void LeftOuterJoinWithOuterRestriction()
164218
{
219+
//TODO: should it be an inner join? As .DefaultIfEmpty() does not make any sense here.
220+
165221
//
166222
// select
167223
// order0_.Id as col_0_0_,
@@ -185,5 +241,33 @@ from ol in o.OrderLines.DefaultIfEmpty().Where(x => x.Name.StartsWith("Order Lin
185241
Assert.AreEqual(2, result.Count);
186242
}
187243
}
244+
245+
[Test]
246+
public void LeftOuterJoinWithOutermostRestriction()
247+
{
248+
//
249+
// select
250+
// order0_.Id as col_0_0_,
251+
// orderlines1_.Id as col_1_0_
252+
// from
253+
// Orders order0_
254+
// left outer join
255+
// OrderLines orderlines1_
256+
// on order0_.Id=orderlines1_.OrderId
257+
// where
258+
// orderlines1_.Name like ('Order Line 3')
259+
//
260+
261+
using (var session = OpenSession())
262+
using (session.BeginTransaction())
263+
{
264+
var result = (from o in session.Query<Order>()
265+
from ol in o.OrderLines.DefaultIfEmpty()
266+
where ol.Name.StartsWith("Order Line 3")
267+
select new {OrderId = o.Id, OrderLineId = (Guid?) ol.Id}).ToList();
268+
269+
Assert.AreEqual(2, result.Count);
270+
}
271+
}
188272
}
189273
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
using System;
2+
using System.Linq;
3+
using NHibernate.Cfg.MappingSchema;
4+
using NHibernate.Linq;
5+
using NHibernate.Mapping.ByCode;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.NHSpecificTest.NH3459
9+
{
10+
[TestFixture]
11+
public class Fixture : TestCaseMappingByCode
12+
{
13+
protected override HbmMapping GetMappings()
14+
{
15+
var mapper = new ModelMapper();
16+
mapper.Class<Order>(rc =>
17+
{
18+
rc.Table("Orders");
19+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
20+
rc.Property(x => x.Name);
21+
rc.Set(x => x.OrderLines, m =>
22+
{
23+
m.Inverse(true);
24+
m.Key(k =>
25+
{
26+
k.Column("OrderId");
27+
k.NotNullable(true);
28+
});
29+
m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans));
30+
m.Access(Accessor.NoSetter);
31+
}, m => m.OneToMany());
32+
});
33+
mapper.Class<OrderLine>(rc =>
34+
{
35+
rc.Table("OrderLines");
36+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
37+
rc.Property(x => x.Manufacturer);
38+
rc.ManyToOne(x => x.Order, m => m.Column("OrderId"));
39+
});
40+
41+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
42+
}
43+
44+
protected override void OnSetUp()
45+
{
46+
using (var session = OpenSession())
47+
using (var transaction = session.BeginTransaction())
48+
{
49+
var o1 = new Order {Name = "Order 1"};
50+
session.Save(o1);
51+
52+
var o2 = new Order {Name = "Order 2"};
53+
session.Save(o2);
54+
55+
session.Save(new OrderLine {Manufacturer = "Manufacturer 1", Order = o2});
56+
57+
var o3 = new Order {Name = "Order 3"};
58+
session.Save(o3);
59+
60+
session.Save(new OrderLine {Manufacturer = "Manufacturer 1", Order = o3});
61+
session.Save(new OrderLine {Manufacturer = "Manufacturer 2", Order = o3});
62+
session.Save(new OrderLine {Manufacturer = "Manufacturer 3", Order = o3});
63+
64+
transaction.Commit();
65+
}
66+
}
67+
68+
protected override void OnTearDown()
69+
{
70+
using (var session = OpenSession())
71+
using (var transaction = session.BeginTransaction())
72+
{
73+
session.Delete("from System.Object");
74+
75+
session.Flush();
76+
transaction.Commit();
77+
}
78+
}
79+
80+
[Test]
81+
public void LeftOuterJoinAndGroupBy()
82+
{
83+
using (var session = OpenSession())
84+
using (session.BeginTransaction())
85+
{
86+
var result = (from o in session.Query<Order>()
87+
from ol in o.OrderLines.DefaultIfEmpty()
88+
group ol by ol.Manufacturer into grp
89+
select new {grp.Key}).ToList();
90+
91+
Assert.AreEqual(4, result.Count);
92+
}
93+
}
94+
95+
[Test]
96+
public void LeftOuterJoinWithInnerRestrictionAndGroupBy()
97+
{
98+
using (var session = OpenSession())
99+
using (session.BeginTransaction())
100+
{
101+
var result = (from o in session.Query<Order>()
102+
from ol in o.OrderLines.Where(x => x.Manufacturer == "Manufacturer 1").DefaultIfEmpty()
103+
group o by o.Name into grp
104+
select new {grp.Key}).ToList();
105+
106+
Assert.AreEqual(3, result.Count);
107+
}
108+
}
109+
110+
[Test]
111+
public void LeftOuterJoinWithOuterRestrictionAndGroupBy()
112+
{
113+
using (var session = OpenSession())
114+
using (session.BeginTransaction())
115+
{
116+
var result = (from o in session.Query<Order>()
117+
from ol in o.OrderLines.DefaultIfEmpty().Where(x => x.Manufacturer == "Manufacturer 1")
118+
group o by o.Name into grp
119+
select new { grp.Key }).ToList();
120+
121+
Assert.AreEqual(2, result.Count);
122+
}
123+
}
124+
125+
[Test]
126+
public void LeftOuterJoinWithOutermostRestrictionAndGroupBy()
127+
{
128+
using (var session = OpenSession())
129+
using (session.BeginTransaction())
130+
{
131+
var result = (from o in session.Query<Order>()
132+
from ol in o.OrderLines.DefaultIfEmpty()
133+
where ol.Manufacturer == "Manufacturer 1"
134+
group o by o.Name into grp
135+
select new { grp.Key }).ToList();
136+
137+
Assert.AreEqual(2, result.Count);
138+
}
139+
}
140+
141+
[Test]
142+
public void InnerJoinAndGroupBy()
143+
{
144+
using (var session = OpenSession())
145+
using (session.BeginTransaction())
146+
{
147+
var result = (from o in session.Query<Order>()
148+
from ol in o.OrderLines
149+
group ol by ol.Manufacturer into grp
150+
select new { grp.Key }).ToList();
151+
152+
Assert.AreEqual(3, result.Count);
153+
}
154+
}
155+
156+
[Test]
157+
public void InnerJoinWithRestrictionAndGroupBy()
158+
{
159+
using (var session = OpenSession())
160+
using (session.BeginTransaction())
161+
{
162+
var result = (from o in session.Query<Order>()
163+
from ol in o.OrderLines.Where(x => x.Manufacturer == "Manufacturer 1")
164+
group o by o.Name into grp
165+
select new { grp.Key }).ToList();
166+
167+
Assert.AreEqual(2, result.Count);
168+
}
169+
}
170+
171+
[Test]
172+
public void InnerJoinWithOutermostRestrictionAndGroupBy()
173+
{
174+
using (var session = OpenSession())
175+
using (session.BeginTransaction())
176+
{
177+
var result = (from o in session.Query<Order>()
178+
from ol in o.OrderLines
179+
where ol.Manufacturer == "Manufacturer 1"
180+
group o by o.Name into grp
181+
select new { grp.Key }).ToList();
182+
183+
Assert.AreEqual(2, result.Count);
184+
}
185+
}
186+
}
187+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH3459
5+
{
6+
public class Order
7+
{
8+
private readonly IEnumerable<OrderLine> _orderLines = new List<OrderLine>();
9+
public virtual Guid Id { get; set; }
10+
public virtual string Name { get; set; }
11+
12+
public virtual IEnumerable<OrderLine> OrderLines
13+
{
14+
get { return _orderLines; }
15+
}
16+
}
17+
18+
public class OrderLine
19+
{
20+
public virtual Guid Id { get; set; }
21+
public virtual string Manufacturer { get; set; }
22+
public virtual Order Order { get; set; }
23+
}
24+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@
670670
<Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Domain.cs" />
671671
<Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Fixture.cs" />
672672
<Compile Include="Component\Basic\ComponentWithUniqueConstraintTests.cs" />
673+
<Compile Include="NHSpecificTest\NH3459\Fixture.cs" />
674+
<Compile Include="NHSpecificTest\NH3459\Order.cs" />
673675
<Compile Include="NHSpecificTest\NH2692\Fixture.cs" />
674676
<Compile Include="NHSpecificTest\NH2692\Model.cs" />
675677
<Compile Include="NHSpecificTest\NH2772\Model.cs" />

src/NHibernate/Linq/Visitors/QueryModelVisitor.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ public class QueryModelVisitor : QueryModelVisitorBase
2020
{
2121
public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, bool root)
2222
{
23-
SubQueryFromClauseFlattener.ReWrite(queryModel);
24-
25-
LeftJoinRewriter.ReWrite(queryModel);
26-
2723
NestedSelectRewriter.ReWrite(queryModel, parameters.SessionFactory);
2824

2925
// Remove unnecessary body operators
@@ -44,6 +40,11 @@ public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel quer
4440
// Rewrite non-aggregating group-joins
4541
NonAggregatingGroupJoinRewriter.ReWrite(queryModel);
4642

43+
SubQueryFromClauseFlattener.ReWrite(queryModel);
44+
45+
// Rewrite left-joins
46+
LeftJoinRewriter.ReWrite(queryModel);
47+
4748
// Rewrite paging
4849
PagingRewriter.ReWrite(queryModel);
4950

0 commit comments

Comments
 (0)