Skip to content

Commit e80ee97

Browse files
committed
Fix dependent association joins
1 parent bf80075 commit e80ee97

File tree

6 files changed

+89
-1
lines changed

6 files changed

+89
-1
lines changed

src/NHibernate.Test/Async/Linq/ByMethod/JoinSubqueryTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,5 +946,30 @@ private void AssertDuplicateEntitySelectionSubQuery(string sql, IList result, bo
946946
}
947947

948948
#endregion
949+
950+
#region DependentEntityJoin
951+
952+
[Test]
953+
public async Task HqlDependentEntityJoinAsync()
954+
{
955+
await (AssertDependentEntityJoinAsync(@"from Order o
956+
inner join (from Order where OrderId = :id) o2 on (o.OrderId - 1) = o2.OrderId
957+
left join o.Employee e on e = o2.Employee"));
958+
}
959+
960+
private async Task AssertDependentEntityJoinAsync(string query, CancellationToken cancellationToken = default(CancellationToken))
961+
{
962+
var result = await (session.CreateQuery(query).SetParameter("id", 10248).ListAsync(cancellationToken));
963+
Assert.That(result, Has.Count.EqualTo(1));
964+
var item = result[0];
965+
Assert.That(item, Is.TypeOf<object[]>());
966+
var array = (object[]) item;
967+
Assert.That(array, Has.Length.EqualTo(3));
968+
Assert.That(array[0], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10249));
969+
Assert.That(array[1], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10248));
970+
Assert.That(array[2], Is.Null);
971+
}
972+
973+
#endregion
949974
}
950975
}

src/NHibernate.Test/Linq/ByMethod/JoinSubqueryTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,5 +934,30 @@ private void AssertDuplicateEntitySelectionSubQuery(string sql, IList result, bo
934934
}
935935

936936
#endregion
937+
938+
#region DependentEntityJoin
939+
940+
[Test]
941+
public void HqlDependentEntityJoin()
942+
{
943+
AssertDependentEntityJoin(@"from Order o
944+
inner join (from Order where OrderId = :id) o2 on (o.OrderId - 1) = o2.OrderId
945+
left join o.Employee e on e = o2.Employee");
946+
}
947+
948+
private void AssertDependentEntityJoin(string query)
949+
{
950+
var result = session.CreateQuery(query).SetParameter("id", 10248).List();
951+
Assert.That(result, Has.Count.EqualTo(1));
952+
var item = result[0];
953+
Assert.That(item, Is.TypeOf<object[]>());
954+
var array = (object[]) item;
955+
Assert.That(array, Has.Length.EqualTo(3));
956+
Assert.That(array[0], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10249));
957+
Assert.That(array[1], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10248));
958+
Assert.That(array[2], Is.Null);
959+
}
960+
961+
#endregion
937962
}
938963
}

src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,7 @@ private void HandleWithFragment(FromElement fromElement, IASTNode hqlWithNode)
13201320
sql.whereExpr();
13211321

13221322
fromElement.WithClauseFragment = new SqlString("(", sql.GetSQL(), ")");
1323+
fromElement.WithClauseFromElements = visitor.FromElements;
13231324
}
13241325
catch (SemanticException)
13251326
{
@@ -1345,6 +1346,8 @@ public WithClauseVisitor(FromElement fromElement)
13451346
_joinFragment = fromElement;
13461347
}
13471348

1349+
internal HashSet<FromElement> FromElements { get; } = new HashSet<FromElement>();
1350+
13481351
public void Visit(IASTNode node)
13491352
{
13501353
if (node is ParameterNode paramNode)
@@ -1355,6 +1358,10 @@ public void Visit(IASTNode node)
13551358
{
13561359
ApplyParameterSpecifications(paramContainer);
13571360
}
1361+
else if (node is ISelectExpression selectExpression && selectExpression.FromElement != null)
1362+
{
1363+
FromElements.Add(selectExpression.FromElement);
1364+
}
13581365
}
13591366

13601367
private void ApplyParameterSpecifications(IParameterContainer parameterContainer)

src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ fromTable
195195
tableJoin [ IASTNode parent ]
196196
: ^( c=JOIN_FRAGMENT { Out(" "); Out($c); } (tableJoin [ c ] )* )
197197
| ^( d=FROM_FRAGMENT { NestedFromFragment($d,parent); } (tableJoin [ d ] )* )
198+
| ^( e=ENTITY_JOIN { Out(" "); Out(e); } (tableJoin [ e ])* )
199+
| ^( f=JOIN_SUBQUERY { Out(" "); StartJoinSubquery(); } selectStatement { EndJoinSubquery(f); } (tableJoin [ f ])* )
198200
;
199201
200202
booleanOp[ bool parens ]

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,36 @@ internal void FinishInit()
436436
{
437437
foreach (var item in _appendFromElements)
438438
{
439-
AddChild(item);
439+
var dependentElement = GetFirstDependentFromElement(item);
440+
if (dependentElement == null)
441+
{
442+
AddChild(item);
443+
}
444+
else
445+
{
446+
var index = dependentElement.ChildIndex;
447+
dependentElement.Parent.InsertChild(index, item);
448+
}
440449
}
441450
_appendFromElements.Clear();
442451
}
452+
453+
private FromElement GetFirstDependentFromElement(FromElement element)
454+
{
455+
foreach (var fromElement in _fromElements)
456+
{
457+
if (fromElement == element ||
458+
fromElement.WithClauseFromElements?.Contains(element) != true ||
459+
// Parent will be null for entity and subquery joins
460+
fromElement.Parent == null)
461+
{
462+
continue;
463+
}
464+
465+
return fromElement;
466+
}
467+
468+
return null;
469+
}
443470
}
444471
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ public SqlString WithClauseFragment
330330
set { _withClauseFragment = value; }
331331
}
332332

333+
internal HashSet<FromElement> WithClauseFromElements { get; set; }
334+
333335
// Since 5.4
334336
[Obsolete("This method has no more usages and will be removed in a future version.")]
335337
public string WithClauseJoinAlias

0 commit comments

Comments
 (0)