Skip to content

Commit 6f882fb

Browse files
committed
Fix broken tests
1 parent 3e5261d commit 6f882fb

File tree

4 files changed

+156
-39
lines changed

4 files changed

+156
-39
lines changed

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

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections;
2+
using System.Linq;
23
using NHibernate.DomainModel.Northwind.Entities;
34
using NUnit.Framework;
45
using NUnit.Framework.Constraints;
@@ -745,7 +746,7 @@ inner join fetch o1.OrderLines
745746
select o, o2.ord
746747
from Order o
747748
inner join (
748-
select o1 as ord, o1.OrderLines.size
749+
select o1 as ord, o1.ShippedTo
749750
from Order o1
750751
inner join fetch o1.OrderLines
751752
where o1.OrderId = 10248
@@ -810,7 +811,7 @@ inner join fetch o1.ProductIds
810811
select o, o2.ord
811812
from Order o
812813
inner join (
813-
select o1 as ord, o1.OrderLines.size
814+
select o1 as ord, o1.ShippedTo
814815
from Order o1
815816
inner join fetch o1.ProductIds
816817
where o1.OrderId = 10248
@@ -854,5 +855,86 @@ private void AssertEntitySubQueryWithCollectionOfValuesFetch(IList result, bool[
854855
}
855856

856857
#endregion
858+
859+
#region HqlDuplicateEntitySelectionSubQuery
860+
861+
[Test]
862+
public void HqlDuplicateEntitySelectionSubQuery()
863+
{
864+
AssertDuplicateEntitySelectionSubQuery(@"
865+
from Order o
866+
inner join (
867+
select o1 as ord1, o1 as ord2
868+
from Order o1
869+
where o1.OrderId = 10248
870+
) o2 on (o.OrderId - 1) = o2.ord1.OrderId");
871+
872+
AssertDuplicateEntitySelectionSubQuery(@"
873+
select o, o2.ord1, o2.ord2
874+
from Order o
875+
inner join (
876+
select o1 as ord1, o1 as ord2
877+
from Order o1
878+
where o1.OrderId = 10248
879+
) o2 on (o.OrderId - 1) = o2.ord1.OrderId");
880+
881+
AssertDuplicateEntitySelectionSubQuery(@"
882+
select o, o2, o2
883+
from Order o
884+
inner join (
885+
select o1
886+
from Order o1
887+
where o1.OrderId = 10248
888+
) o2 on (o.OrderId - 1) = o2.OrderId");
889+
890+
AssertDuplicateEntitySelectionSubQuery(@"
891+
select o, o2, o2
892+
from Order o
893+
inner join (
894+
from Order
895+
where OrderId = 10248
896+
) o2 on (o.OrderId - 1) = o2.OrderId");
897+
}
898+
899+
private void AssertDuplicateEntitySelectionSubQuery(string query)
900+
{
901+
IList result;
902+
using (var logSpy = new SqlLogSpy())
903+
{
904+
result = session.CreateQuery(query).List();
905+
AssertDuplicateEntitySelectionSubQuery(logSpy.GetWholeLog(), result, false);
906+
}
907+
908+
using (var logSpy = new SqlLogSpy())
909+
{
910+
result = session.CreateQuery(query).Enumerable().OfType<object[]>().ToList();
911+
AssertDuplicateEntitySelectionSubQuery(logSpy.GetWholeLog(), result, true);
912+
}
913+
}
914+
915+
private void AssertDuplicateEntitySelectionSubQuery(string sql, IList result, bool shallow)
916+
{
917+
var selectSql = sql.Substring(0, sql.IndexOf("from"));
918+
var item = result[0];
919+
Assert.That(item, Is.TypeOf<object[]>());
920+
var array = (object[]) item;
921+
Assert.That(array, Has.Length.EqualTo(3));
922+
if (shallow)
923+
{
924+
Assert.That(GetTotalOccurrences(selectSql, ","), Is.EqualTo(1));
925+
Assert.That(array[0], Is.AssignableFrom<Order>().And.Property("OrderId").EqualTo(10249));
926+
Assert.That(array[1], Is.AssignableFrom<Order>().And.Property("OrderId").EqualTo(10248));
927+
Assert.That(array[1], Is.AssignableFrom<Order>().And.Property("OrderId").EqualTo(10248));
928+
}
929+
else
930+
{
931+
Assert.That(GetTotalOccurrences(selectSql, ","), Is.EqualTo(27));
932+
Assert.That(array[0], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10249));
933+
Assert.That(array[1], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10248));
934+
Assert.That(array[1], Is.TypeOf<Order>().And.Property("OrderId").EqualTo(10248));
935+
}
936+
}
937+
938+
#endregion
857939
}
858940
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ public bool IsShallowQuery
12371237
get
12381238
{
12391239
// select clauses for insert statements should alwasy be treated as shallow
1240-
return StatementType == INSERT || _qti.IsShallowQuery;
1240+
return StatementType == INSERT || (_qti.IsShallowQuery && !_currentFromClause.IsJoinSubQuery);
12411241
}
12421242
}
12431243

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

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ public void InitializeExplicitSelectClause(FromClause fromClause)
103103
var inheritedExpressions = new Dictionary<ISelectExpression, SelectClause>();
104104
SelectExpressions = GetSelectExpressions();
105105
OriginalSelectExpressions = SelectExpressions.ToList();
106-
NonScalarExpressions = !Walker.IsShallowQuery
107-
? new List<ISelectExpression>()
108-
: null;
106+
NonScalarExpressions = new List<ISelectExpression>();
109107
var length = SelectExpressions.Count;
110108
for (var i = 0; i < length; i++)
111109
{
@@ -152,7 +150,7 @@ public void InitializeExplicitSelectClause(FromClause fromClause)
152150

153151
if (!selectClause.IsScalarSelect)
154152
{
155-
RemoveChild((IASTNode) expr);
153+
RemoveChildAndUnsetParent((IASTNode) expr);
156154
}
157155

158156
subqueryExpressions = new List<ISelectExpression>();
@@ -180,7 +178,7 @@ public void InitializeExplicitSelectClause(FromClause fromClause)
180178
var indexes = new List<int>(subqueryExpressions.Count);
181179
foreach (var expression in subqueryExpressions)
182180
{
183-
inheritedExpressions.Add(expression, selectClause);
181+
inheritedExpressions[expression] = selectClause;
184182
indexes.Add(i);
185183
SelectExpressions.Insert(i, expression);
186184
i++;
@@ -227,16 +225,21 @@ private void Render(
227225
InitializeScalarColumnNames();
228226
}
229227

228+
// generate id select fragment and then property select fragment for
229+
// each expression, just like generateSelectFragments().
230+
RenderNonScalarSelects(fromClause, inheritedExpressions, GetFetchedFromElements(fromClause));
231+
}
232+
233+
private List<FromElement> GetFetchedFromElements(FromClause fromClause)
234+
{
235+
var fetchedFromElements = new List<FromElement>();
230236
if (Walker.IsShallowQuery)
231237
{
232-
RenderDerivedNonScalarIdentifiers(fromClause);
233-
return;
238+
return fetchedFromElements;
234239
}
235240

236-
var fetchedFromElements = new List<FromElement>();
237241
// add the fetched entities
238-
var fromElements = fromClause.GetAllProjectionListTyped();
239-
foreach (FromElement fromElement in fromElements)
242+
foreach (FromElement fromElement in fromClause.GetAllProjectionListTyped())
240243
{
241244
if (!fromElement.IsFetch)
242245
{
@@ -278,9 +281,7 @@ private void Render(
278281
}
279282
}
280283

281-
// generate id select fragment and then property select fragment for
282-
// each expression, just like generateSelectFragments().
283-
RenderNonScalarSelects(fromClause, inheritedExpressions, fetchedFromElements);
284+
return fetchedFromElements;
284285
}
285286

286287
private void AddExpression(ISelectExpression expr, List<IType> queryReturnTypeList)
@@ -452,19 +453,40 @@ private void RenderNonScalarSelects(
452453
{
453454
var appender = new ASTAppender(ASTFactory, this);
454455
var combinedFromElements = new List<FromElement>();
456+
var processedElements = new HashSet<FromElement>();
455457
foreach (var e in NonScalarExpressions)
456458
{
457459
var fromElement = e.FromElement;
458-
if (fromElement != null)
460+
if (fromElement == null)
461+
{
462+
continue;
463+
}
464+
465+
var node = (IASTNode) e;
466+
if (processedElements.Add(fromElement))
459467
{
460468
combinedFromElements.Add(fromElement);
461469
RenderNonScalarIdentifiers(fromElement, inheritedExpressions.ContainsKey(e) ? null : e, appender);
462470
}
471+
else if (!inheritedExpressions.ContainsKey(e) && node.Parent != null)
472+
{
473+
RemoveChildAndUnsetParent(node);
474+
}
475+
}
476+
477+
if (Walker.IsShallowQuery)
478+
{
479+
return;
463480
}
464481

465482
// Append fetched elements
466483
foreach (var fetchedFromElement in fetchedFromElements)
467484
{
485+
if (!processedElements.Add(fetchedFromElement))
486+
{
487+
continue;
488+
}
489+
468490
fetchedFromElement.EntitySuffix = Walker.GetEntitySuffix(fetchedFromElement);
469491
combinedFromElements.Add(fetchedFromElement);
470492
var fragment = fetchedFromElement.GetIdentifierSelectFragment(fetchedFromElement.EntitySuffix);
@@ -495,7 +517,9 @@ private void RenderNonScalarSelects(
495517
var fromElements = currentFromClause.GetAllProjectionListTyped();
496518
foreach (var fromElement in fromElements)
497519
{
498-
if (fromElement.IsCollectionOfValuesOrComponents && fromElement.IsFetch)
520+
if (fromElement.IsCollectionOfValuesOrComponents &&
521+
fromElement.IsFetch &&
522+
processedElements.Add(fromElement))
499523
{
500524
var suffix = Walker.GetSuffix(fromElement);
501525
var fragment = fromElement.GetValueCollectionSelectFragment(suffix);
@@ -514,25 +538,16 @@ private IASTNode Append(ASTAppender appender, int type, SelectFragment fragment)
514538
return appender.Append(type, fragment.ToSqlStringFragment(false), false);
515539
}
516540

517-
private void RenderDerivedNonScalarIdentifiers(FromClause fromClause)
541+
private void RenderNonScalarIdentifiers(FromElement fromElement, ISelectExpression expr, ASTAppender appender)
518542
{
519-
// Render only when scalar columns are not rendered
520-
if (_derivedSelectExpressions == null || !fromClause.IsScalarSubQuery)
543+
if (fromElement.FromClause.IsScalarSubQuery && _derivedSelectExpressions?.Contains(expr) != true)
521544
{
522545
return;
523546
}
524547

525-
var appender = new ASTAppender(ASTFactory, this);
526-
foreach (var derivedSelectExpression in _derivedSelectExpressions)
527-
{
528-
RenderNonScalarIdentifiers(derivedSelectExpression.FromElement, derivedSelectExpression, appender);
529-
}
530-
}
531-
532-
private void RenderNonScalarIdentifiers(FromElement fromElement, ISelectExpression expr, ASTAppender appender)
533-
{
534-
if (fromElement.FromClause.IsScalarSubQuery && _derivedSelectExpressions?.Contains(expr) != true)
548+
if (Walker.IsShallowQuery && !fromElement.FromClause.IsScalarSubQuery && SelectExpressions.Contains(expr))
535549
{
550+
// A scalar column was generated
536551
return;
537552
}
538553

@@ -541,8 +556,7 @@ private void RenderNonScalarIdentifiers(FromElement fromElement, ISelectExpressi
541556
if (fragment == null)
542557
{
543558
// When a subquery join has a scalar select only
544-
var node = (IASTNode) expr;
545-
node?.Parent.RemoveChild(node);
559+
RemoveChildAndUnsetParent((IASTNode) expr);
546560
return;
547561
}
548562

@@ -706,5 +720,14 @@ public int GetColumnNamesStartPosition(int i)
706720
{
707721
return _columnNamesStartPositions[i];
708722
}
723+
724+
private static void RemoveChildAndUnsetParent(IASTNode node)
725+
{
726+
if (node?.Parent != null)
727+
{
728+
node.Parent.RemoveChild(node);
729+
node.Parent = null;
730+
}
731+
}
709732
}
710733
}

src/NHibernate/Persister/Entity/SubqueryPropertyMapping.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ public SubqueryPropertyMapping(IType type, SelectClause selectClause)
7373
}
7474
else
7575
{
76+
if (_propertyMappings.ContainsKey(mapping))
77+
{
78+
throw new QueryException($"Subquery selects the same type '{mapping.Type}' multiple times. Use unique alias for each type selection.");
79+
}
80+
7681
_propertyMappings.Add(mapping, fromElement.EntitySuffix ?? fromElement.CollectionSuffix);
7782
}
7883
}
@@ -189,15 +194,17 @@ public string[] ToColumns(string alias, string propertyName)
189194
public List<string> GetPropertiesColumns(string alias)
190195
{
191196
var columns = new List<string>();
197+
var processedElements = new HashSet<FromElement>();
192198
foreach (var expression in _selectClause.NonScalarExpressions)
193199
{
194-
if (expression.FromElement == null || expression.FromElement.IsFetch)
200+
var fromElement = expression.FromElement;
201+
if (fromElement == null || fromElement.IsFetch || !processedElements.Add(fromElement))
195202
{
196203
continue;
197204
}
198205

199-
var fragment = expression.FromElement.GetPropertiesSelectFragment(expression.FromElement.EntitySuffix, alias);
200-
if (expression.FromElement is JoinSubqueryFromElement)
206+
var fragment = fromElement.GetPropertiesSelectFragment(fromElement.EntitySuffix, alias);
207+
if (fromElement is JoinSubqueryFromElement)
201208
{
202209
columns.AddRange(fragment.GetColumnAliases());
203210
}
@@ -221,20 +228,25 @@ public List<string> GetPropertiesColumns(string alias)
221228
public List<string> GetIdentifiersColumns(string alias)
222229
{
223230
var columns = new List<string>();
231+
var processedElements = new HashSet<FromElement>();
224232
foreach (var expression in _selectClause.NonScalarExpressions)
225233
{
226-
if (expression.FromElement == null || expression.FromElement.FromClause.IsScalarSubQuery || expression.FromElement.IsFetch)
234+
var fromElement = expression.FromElement;
235+
if (fromElement == null ||
236+
fromElement.FromClause.IsScalarSubQuery ||
237+
fromElement.IsFetch ||
238+
!processedElements.Add(fromElement))
227239
{
228240
continue;
229241
}
230242

231-
var fragment = expression.FromElement.GetIdentifierSelectFragment(expression.FromElement.EntitySuffix, alias);
243+
var fragment = fromElement.GetIdentifierSelectFragment(fromElement.EntitySuffix, alias);
232244
if (fragment == null)
233245
{
234246
continue; // Subquery with scalar select
235247
}
236248

237-
if (expression.FromElement is JoinSubqueryFromElement)
249+
if (fromElement is JoinSubqueryFromElement)
238250
{
239251
columns.AddRange(fragment.GetColumnAliases());
240252
}

0 commit comments

Comments
 (0)