Skip to content

Add cross join support for Hql and Linq query provider #2327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 21, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,27 @@ public async Task EntityJoinWithFetchesAsync()
}
}

[Test]
public async Task CrossJoinAndWhereClauseAsync()
{
using (var sqlLog = new SqlLogSpy())
using (var session = OpenSession())
{
var result = await (session.CreateQuery(
"SELECT s " +
"FROM EntityComplex s cross join EntityComplex q " +
"where s.SameTypeChild.Id = q.SameTypeChild.Id"
).ListAsync());

Assert.That(result, Has.Count.EqualTo(1));
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
if (Dialect.SupportsCrossJoin)
{
Assert.That(sqlLog.GetWholeLog(), Does.Contain("cross join"), "A cross join is expected in the SQL select");
}
}
}

#region Test Setup

protected override HbmMapping GetMappings()
Expand Down
33 changes: 33 additions & 0 deletions src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
//------------------------------------------------------------------------------


using System;
using System.Linq;
using System.Reflection;
using NHibernate.Cfg;
using NHibernate.Engine.Query;
using NHibernate.Util;
using NSubstitute;
using NUnit.Framework;
using NHibernate.Linq;

Expand All @@ -31,5 +37,32 @@ public async Task MultipleLinqJoinsWithSameProjectionNamesAsync()
Assert.That(orders.Count, Is.EqualTo(828));
Assert.IsTrue(orders.All(x => x.FirstId == x.SecondId - 1 && x.SecondId == x.ThirdId - 1));
}

[TestCase(false)]
[TestCase(true)]
public async Task CrossJoinWithPredicateInWhereStatementAsync(bool useCrossJoin)
{
if (useCrossJoin && !Dialect.SupportsCrossJoin)
{
Assert.Ignore("Dialect does not support cross join.");
}

using (var substitute = SubstituteDialect())
using (var sqlSpy = new SqlLogSpy())
{
ClearQueryPlanCache();
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);

var result = await ((from o in db.Orders
from o2 in db.Orders.Where(x => x.Freight > 50)
where (o.OrderId == o2.OrderId + 1) || (o.OrderId == o2.OrderId - 1)
select new { o.OrderId, OrderId2 = o2.OrderId }).ToListAsync());

var sql = sqlSpy.GetWholeLog();
Assert.That(result.Count, Is.EqualTo(720));
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(useCrossJoin ? 0 : 1));
}
}
}
}
144 changes: 123 additions & 21 deletions src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NHibernate.DomainModel.Northwind.Entities;
using NSubstitute;
using NUnit.Framework;
using NHibernate.Linq;

Expand Down Expand Up @@ -757,7 +759,13 @@ from o in c.Orders
where c.Address.City == "London"
select o;

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
Expand Down Expand Up @@ -863,7 +871,13 @@ from p in db.Products
where p.Supplier.Address.Country == "USA" && p.UnitsInStock == 0
select p;

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
Expand All @@ -879,7 +893,16 @@ from et in e.Territories
where e.Address.City == "Seattle"
select new {e.FirstName, e.LastName, et.Region.Description};

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
// EmployeeTerritories and Territories
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(2));
// Region
Assert.That(GetTotalOccurrences(sql, "left outer join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
Expand All @@ -903,7 +926,13 @@ from e2 in e1.Subordinates
e1.Address.City
};

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
Expand All @@ -918,7 +947,13 @@ from c in db.Customers
join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into orders
select new {c.ContactName, OrderCount = orders.Average(x => x.Freight)};

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "join"), Is.EqualTo(0));
}
}

[Category("JOIN")]
Expand Down Expand Up @@ -959,15 +994,51 @@ from c in db.Customers
}

[Category("JOIN")]
[Test(Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
public async Task DLinqJoin5dAsync()
[TestCase(true, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
[TestCase(false, Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
public async Task DLinqJoin5dAsync(bool useCrossJoin)
{
if (useCrossJoin && !Dialect.SupportsCrossJoin)
{
Assert.Ignore("Dialect does not support cross join.");
}

var q =
from c in db.Customers
join o in db.Orders on new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null }
select new { c.ContactName, o.OrderId };

await (ObjectDumper.WriteAsync(q));
using (var substitute = SubstituteDialect())
using (var sqlSpy = new SqlLogSpy())
{
ClearQueryPlanCache();
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);

await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
Assert.That(GetTotalOccurrences(sql, "left outer join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
[Test(Description = "This sample joins two tables and projects results from the first table.")]
public async Task DLinqJoin5eAsync()
{
var q =
from c in db.Customers
join o in db.Orders on c.CustomerId equals o.Customer.CustomerId
where c.ContactName != null
select o;

using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
Expand All @@ -983,7 +1054,13 @@ join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into ords
join e in db.Employees on c.Address.City equals e.Address.City into emps
select new {c.ContactName, ords = ords.Count(), emps = emps.Count()};

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "join"), Is.EqualTo(0));
}
}

[Category("JOIN")]
Expand All @@ -997,14 +1074,27 @@ join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into ords
from o in ords
select new {c.ContactName, o.OrderId, z};

await (ObjectDumper.WriteAsync(q));
using (var sqlSpy = new SqlLogSpy())
{
await (ObjectDumper.WriteAsync(q));

var sql = sqlSpy.GetWholeLog();
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(1));
}
}

[Category("JOIN")]
[Test(Description = "This sample shows a group join with a composite key.")]
public async Task DLinqJoin9Async()
[TestCase(true, Description = "This sample shows a group join with a composite key.")]
[TestCase(false, Description = "This sample shows a group join with a composite key.")]
public async Task DLinqJoin9Async(bool useCrossJoin)
{
var expected =
if (useCrossJoin && !Dialect.SupportsCrossJoin)
{
Assert.Ignore("Dialect does not support cross join.");
}

ICollection expected, actual;
expected =
(from o in db.Orders.ToList()
from p in db.Products.ToList()
join d in db.OrderLines.ToList()
Expand All @@ -1013,14 +1103,26 @@ into details
from d in details
select new {o.OrderId, p.ProductId, d.UnitPrice}).ToList();

var actual =
await ((from o in db.Orders
from p in db.Products
join d in db.OrderLines
on new {o.OrderId, p.ProductId} equals new {d.Order.OrderId, d.Product.ProductId}
into details
from d in details
select new {o.OrderId, p.ProductId, d.UnitPrice}).ToListAsync());
using (var substitute = SubstituteDialect())
using (var sqlSpy = new SqlLogSpy())
{
ClearQueryPlanCache();
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);

actual =
await ((from o in db.Orders
from p in db.Products
join d in db.OrderLines
on new { o.OrderId, p.ProductId } equals new { d.Order.OrderId, d.Product.ProductId }
into details
from d in details
select new { o.OrderId, p.ProductId, d.UnitPrice }).ToListAsync());

var sql = sqlSpy.GetWholeLog();
Assert.That(actual.Count, Is.EqualTo(2155));
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(useCrossJoin ? 1 : 2));
}

Assert.AreEqual(expected.Count, actual.Count);
}
Expand Down
23 changes: 22 additions & 1 deletion src/NHibernate.Test/Hql/EntityJoinHqlTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public void EntityJoinWithFetches()
}

[Test, Ignore("Failing for unrelated reasons")]
public void CrossJoinAndWithClause()
public void ImplicitJoinAndWithClause()
{
//This is about complex theta style join fix that was implemented in hibernate along with entity join functionality
//https://hibernate.atlassian.net/browse/HHH-7321
Expand All @@ -279,6 +279,27 @@ public void CrossJoinAndWithClause()
}
}

[Test]
public void CrossJoinAndWhereClause()
{
using (var sqlLog = new SqlLogSpy())
using (var session = OpenSession())
{
var result = session.CreateQuery(
"SELECT s " +
"FROM EntityComplex s cross join EntityComplex q " +
"where s.SameTypeChild.Id = q.SameTypeChild.Id"
).List();

Assert.That(result, Has.Count.EqualTo(1));
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
if (Dialect.SupportsCrossJoin)
{
Assert.That(sqlLog.GetWholeLog(), Does.Contain("cross join"), "A cross join is expected in the SQL select");
}
}
}

#region Test Setup

protected override HbmMapping GetMappings()
Expand Down
35 changes: 34 additions & 1 deletion src/NHibernate.Test/Linq/ByMethod/JoinTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
using System.Linq;
using System;
using System.Linq;
using System.Reflection;
using NHibernate.Cfg;
using NHibernate.Engine.Query;
using NHibernate.Util;
using NSubstitute;
using NUnit.Framework;

namespace NHibernate.Test.Linq.ByMethod
Expand All @@ -19,5 +25,32 @@ public void MultipleLinqJoinsWithSameProjectionNames()
Assert.That(orders.Count, Is.EqualTo(828));
Assert.IsTrue(orders.All(x => x.FirstId == x.SecondId - 1 && x.SecondId == x.ThirdId - 1));
}

[TestCase(false)]
[TestCase(true)]
public void CrossJoinWithPredicateInWhereStatement(bool useCrossJoin)
{
if (useCrossJoin && !Dialect.SupportsCrossJoin)
{
Assert.Ignore("Dialect does not support cross join.");
}

using (var substitute = SubstituteDialect())
using (var sqlSpy = new SqlLogSpy())
{
ClearQueryPlanCache();
substitute.Value.SupportsCrossJoin.Returns(useCrossJoin);

var result = (from o in db.Orders
from o2 in db.Orders.Where(x => x.Freight > 50)
where (o.OrderId == o2.OrderId + 1) || (o.OrderId == o2.OrderId - 1)
select new { o.OrderId, OrderId2 = o2.OrderId }).ToList();

var sql = sqlSpy.GetWholeLog();
Assert.That(result.Count, Is.EqualTo(720));
Assert.That(sql, Does.Contain(useCrossJoin ? "cross join" : "inner join"));
Assert.That(GetTotalOccurrences(sql, "inner join"), Is.EqualTo(useCrossJoin ? 0 : 1));
}
}
}
}
Loading