未验证 提交 38750112 编写于 作者: R Roman Artiukhin 提交者: GitHub

Support association joins from main query to be used in subqueries (#3369)

Fixes #3334
Co-authored-by: NCSharper2010 <csharper2010@googlemail.com>
Co-authored-by: NFrédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com>
上级 0fdb589b
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.GH1228
{
using System.Threading.Tasks;
[TestFixture]
public class FixtureAsync : BugTestCase
{
[Test]
public async Task TestThetaJoinOnAssociationInSubQueryAsync()
{
using var s = OpenSession();
var queryThatWorks = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
, ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
await (queryThatWorks.ListAsync());
queryThatWorks = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
, ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
await (queryThatWorks.ListAsync());
}
[Test]
public async Task TestAnsiJoinOnAssociationInSubQueryAsync()
{
if (!TestDialect.SupportsCorrelatedColumnsInSubselectJoin)
Assert.Ignore("Dialect doesn't support this test case");
using var s = OpenSession();
var queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
await (queryThatCreatesWrongSQL.ListAsync());
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
await (queryThatCreatesWrongSQL.ListAsync());
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
}
[Test]
public async Task TestOtherAnsiJoinOnAssociationInSubQueryAsync()
{
using var s = OpenSession();
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
var queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN sheet.Folder AS folder
WHERE folder.Shelf = ROOT AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
await (queryThatCreatesWrongSQL.ListAsync());
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN inv.Folders AS folder
WHERE folder = ROOT.Folder AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
await (queryThatCreatesWrongSQL.ListAsync());
}
}
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.GH3334
{
using System.Threading.Tasks;
[TestFixture]
public class FixtureAsync : BugTestCase
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
using var session = OpenSession();
using var t = session.BeginTransaction();
var parent = new Entity
{
Name = "Parent1",
Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "GrandChild" } } }
};
session.Save(parent);
parent = new Entity
{
Name = "Parent2",
Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "XGrandChild" } } }
};
var other = new OtherEntity { Name = "ABC", Entities = {parent}};
parent.OtherEntity = other;
session.Save(parent);
session.Save(other);
t.Commit();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
session.CreateQuery("delete from GrandChildEntity").ExecuteUpdate();
session.CreateQuery("delete from Entity").ExecuteUpdate();
session.CreateQuery("delete from OtherEntity").ExecuteUpdate();
transaction.Commit();
}
protected override bool AppliesTo(Dialect.Dialect dialect)
{
return TestDialect.SupportsCorrelatedColumnsInSubselectJoin;
}
public class TestCaseItem
{
public string Name { get; }
public string Hql { get; }
public int LineNumber { get; }
public TestCaseItem(string name, string hql, [CallerLineNumber] int lineNumber = 0)
{
Name = name;
Hql = hql;
LineNumber = lineNumber;
}
public override string ToString() => $"{LineNumber:0000}: {Name}";
}
public static IEnumerable<TestCaseItem> GetNoExceptionOnExecuteQueryTestCases()
{
/* does not work because of inner join or theta join created for many-to-one
@"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
WHERE
child.Child.Name like 'G%'
OR ROOT.OtherEntity.Name like 'A%'
)");*/
yield return new("Basic Elements case 1 FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'G%'
)");
yield return new("Basic Elements case 2 FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.OtherEntity) AS otherEntity
WHERE
otherEntity.Name like 'A%'
)");
yield return new("HQL Elements FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
grandChild.Name like 'G%'
OR otherEntity.Name like 'G%'
)");
yield return new("HQL Elements FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)");
yield return new("HQL Entity FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ChildEntity AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
child.Parent = ROOT
AND (
grandChild.Name like 'G%'
OR otherEntity.Name like 'G%'
)
)");
yield return new("HQL Entity FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ChildEntity AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
child.Parent = ROOT
AND (
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)
)");
yield return new("FROM ROOT.Children FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ROOT.Children AS child
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'G%'
)");
yield return new("FROM ROOT.OtherEntity FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ROOT.OtherEntity AS otherEntity
LEFT JOIN ChildEntity AS child ON child.Parent = ROOT
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)");
}
[Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))]
public async Task NoExceptionOnExecuteQueryAsync(TestCaseItem testCase)
{
using var session = OpenSession();
var q = session.CreateQuery(testCase.Hql);
Assert.That(await (q.ListAsync()), Has.Count.EqualTo(1));
}
protected override bool CheckDatabaseWasCleaned()
{
// same set of objects for each test
return true;
}
}
}
......@@ -2,132 +2,94 @@
namespace NHibernate.Test.NHSpecificTest.GH1228
{
[TestFixture]
public class Fixture : BugTestCase
{
[Test]
public void TestOk()
public void TestThetaJoinOnAssociationInSubQuery()
{
using (ISession s = OpenSession())
{
using (ITransaction t = s.BeginTransaction())
{
try
{
{
var queryThatWorks = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
, ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
) )
AND ROOT.Name = 'SomeName'");
queryThatWorks.List();
}
{
var queryThatWorks = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
, ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
) )
AND ROOT.Id = 1");
queryThatWorks.List();
}
}
finally
{
s.Delete("from Sheet");
s.Delete("from Folder");
s.Delete("from Shelf");
t.Commit();
}
}
}
using var s = OpenSession();
var queryThatWorks = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
, ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
queryThatWorks.List();
queryThatWorks = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
, ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
queryThatWorks.List();
}
[Test]
public void TestWrongSql()
public void TestAnsiJoinOnAssociationInSubQuery()
{
using (ISession s = OpenSession())
{
using (ITransaction t = s.BeginTransaction())
{
try
{
{
var queryThatCreatesWrongSQL = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
) )
AND ROOT.Name = 'SomeName'");
queryThatCreatesWrongSQL.List();
}
{
// The only assertion here is that the generated SQL is valid and can be executed.
// Right now, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
var queryThatCreatesWrongSQL = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
) )
AND ROOT.Id = 1");
queryThatCreatesWrongSQL.List();
// The only assertion here is that the generated SQL is valid and can be executed.
// Right now, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
}
}
finally
{
s.Delete("from Sheet");
s.Delete("from Folder");
s.Delete("from Shelf");
t.Commit();
}
}
}
if (!TestDialect.SupportsCorrelatedColumnsInSubselectJoin)
Assert.Ignore("Dialect doesn't support this test case");
using var s = OpenSession();
var queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN ROOT.Folder AS ROOT_Folder
WHERE ROOT_Folder.Shelf = inv AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
queryThatCreatesWrongSQL.List();
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN ROOT.Folders AS ROOT_Folder
WHERE ROOT_Folder = sheet.Folder AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
queryThatCreatesWrongSQL.List();
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
}
[Test]
public void Test3() {
using (ISession s = OpenSession()) {
using (ITransaction t = s.BeginTransaction()) {
try {
{
// The only assertion here is that the generated SQL is valid and can be executed.
// Right now, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
var queryThatCreatesWrongSQL = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN sheet.Folder AS folder
WHERE folder.Shelf = ROOT AND sheet.Name = 'SomeName'
) )
AND ROOT.Id = 1");
queryThatCreatesWrongSQL.List();
// The only assertion here is that the generated SQL is valid and can be executed.
// Right now, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
}
{
var queryThatCreatesWrongSQL = s.CreateQuery(@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN inv.Folders AS folder
WHERE folder = ROOT.Folder AND inv.Id = 1
) )
AND ROOT.Name = 'SomeName'");
queryThatCreatesWrongSQL.List();
}
}
finally {
s.Delete("from Sheet");
s.Delete("from Folder");
s.Delete("from Shelf");
t.Commit();
}
}
}
public void TestOtherAnsiJoinOnAssociationInSubQuery()
{
using var s = OpenSession();
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
var queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS sheet
JOIN sheet.Folder AS folder
WHERE folder.Shelf = ROOT AND sheet.Name = 'SomeName'
))
AND ROOT.Id = 1");
queryThatCreatesWrongSQL.List();
// The only assertion here is that the generated SQL is valid and can be executed.
// With the bug, the generated SQL is missing the JOIN inside the subselect (EXISTS) to Folder.
queryThatCreatesWrongSQL = s.CreateQuery(
@"
SELECT ROOT FROM NHibernate.Test.NHSpecificTest.GH1228.Sheet AS ROOT
WHERE (EXISTS (FROM NHibernate.Test.NHSpecificTest.GH1228.Shelf AS inv
JOIN inv.Folders AS folder
WHERE folder = ROOT.Folder AND inv.Id = 1
))
AND ROOT.Name = 'SomeName'");
queryThatCreatesWrongSQL.List();
}
}
}
using System.Collections.Generic;
namespace NHibernate.Test.NHSpecificTest.GH3334
{
public class Entity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<ChildEntity> Children { get; set; } = new HashSet<ChildEntity>();
public virtual OtherEntity OtherEntity { get; set; }
}
public class ChildEntity
{
public virtual int Id { get; set; }
public virtual Entity Parent { get; set; }
public virtual string Name { get; set; }
public virtual GrandChildEntity Child { get; set; }
}
public class GrandChildEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class OtherEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<Entity> Entities { get; set; } = new HashSet<Entity>();
}
}
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.GH3334
{
[TestFixture]
public class Fixture : BugTestCase
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
using var session = OpenSession();
using var t = session.BeginTransaction();
var parent = new Entity
{
Name = "Parent1",
Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "GrandChild" } } }
};
session.Save(parent);
parent = new Entity
{
Name = "Parent2",
Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "XGrandChild" } } }
};
var other = new OtherEntity { Name = "ABC", Entities = {parent}};
parent.OtherEntity = other;
session.Save(parent);
session.Save(other);
t.Commit();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
session.CreateQuery("delete from GrandChildEntity").ExecuteUpdate();
session.CreateQuery("delete from Entity").ExecuteUpdate();
session.CreateQuery("delete from OtherEntity").ExecuteUpdate();
transaction.Commit();
}
protected override bool AppliesTo(Dialect.Dialect dialect)
{
return TestDialect.SupportsCorrelatedColumnsInSubselectJoin;
}
public class TestCaseItem
{
public string Name { get; }
public string Hql { get; }
public int LineNumber { get; }
public TestCaseItem(string name, string hql, [CallerLineNumber] int lineNumber = 0)
{
Name = name;
Hql = hql;
LineNumber = lineNumber;
}
public override string ToString() => $"{LineNumber:0000}: {Name}";
}
public static IEnumerable<TestCaseItem> GetNoExceptionOnExecuteQueryTestCases()
{
/* does not work because of inner join or theta join created for many-to-one
@"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
WHERE
child.Child.Name like 'G%'
OR ROOT.OtherEntity.Name like 'A%'
)");*/
yield return new("Basic Elements case 1 FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'G%'
)");
yield return new("Basic Elements case 2 FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.OtherEntity) AS otherEntity
WHERE
otherEntity.Name like 'A%'
)");
yield return new("HQL Elements FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
grandChild.Name like 'G%'
OR otherEntity.Name like 'G%'
)");
yield return new("HQL Elements FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ELEMENTS(ROOT.Children) AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)");
yield return new("HQL Entity FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ChildEntity AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
child.Parent = ROOT
AND (
grandChild.Name like 'G%'
OR otherEntity.Name like 'G%'
)
)");
yield return new("HQL Entity FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ChildEntity AS child
LEFT JOIN child.Child AS grandChild
LEFT JOIN ROOT.OtherEntity AS otherEntity
WHERE
child.Parent = ROOT
AND (
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)
)");
yield return new("FROM ROOT.Children FoundViaGrandChildG", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ROOT.Children AS child
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'G%'
)");
yield return new("FROM ROOT.OtherEntity FoundViaOtherEntityA", @"
SELECT ROOT
FROM Entity AS ROOT
WHERE
EXISTS
(FROM ROOT.OtherEntity AS otherEntity
LEFT JOIN ChildEntity AS child ON child.Parent = ROOT
LEFT JOIN child.Child AS grandChild
WHERE
grandChild.Name like 'A%'
OR otherEntity.Name like 'A%'
)");
}
[Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))]
public void NoExceptionOnExecuteQuery(TestCaseItem testCase)
{
using var session = OpenSession();
var q = session.CreateQuery(testCase.Hql);
Assert.That(q.List(), Has.Count.EqualTo(1));
}
protected override bool CheckDatabaseWasCleaned()
{
// same set of objects for each test
return true;
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
namespace="NHibernate.Test.NHSpecificTest.GH3334">
<class name="Entity">
<id name="Id" generator="identity" />
<property name="Name"/>
<set name="Children" cascade="persist,delete,save-update,evict,lock,replicate,delete-orphan">
<key column="Parent" />
<one-to-many class="ChildEntity"/>
</set>
<many-to-one name="OtherEntity" class="OtherEntity"/>
</class>
<class name="ChildEntity">
<id name="Id" generator="identity" />
<many-to-one name="Parent" class="Entity" column="Parent" />
<property name="Name"/>
<many-to-one name="Child" class="GrandChildEntity" cascade="persist,delete,save-update,evict,lock,replicate,delete-orphan"/>
</class>
<class name="GrandChildEntity">
<id name="Id" generator="identity" />
<property name="Name"/>
</class>
<class name="OtherEntity">
<id name="Id" generator="identity" />
<property name="Name"/>
<set name="Entities" inverse="true" lazy="true">
<key column="OtherEntity" />
<one-to-many class="Entity" />
</set>
</class>
</hibernate-mapping>
......@@ -203,5 +203,10 @@ public bool SupportsSqlType(SqlType sqlType)
/// Returns true if you can cancel a query.
/// </summary>
public virtual bool SupportsCancelQuery => true;
/// <summary>
/// Some databases (MySql) don't support using main table aliases in subquery inside join ON clause
/// </summary>
public virtual bool SupportsCorrelatedColumnsInSubselectJoin => true;
}
}
......@@ -14,5 +14,12 @@ public MySQL5TestDialect(Dialect.Dialect dialect)
/// This behaviour is documented at: http://dev.mysql.com/doc/refman/5.6/en/update.html
/// </summary>
public override bool SupportsModifyAndSelectSameTable => false;
/// <summary>
/// A correlated column can be present only in the subquery's WHERE clause (and not in the SELECT list,
/// a JOIN or ORDER BY clause, a GROUP BY list, or a HAVING clause). Nor can there be any correlated column inside a derived table in the subquery's FROM list.
/// See https://dev.mysql.com/doc/refman/8.0/en/correlated-subqueries.html
/// </summary>
public override bool SupportsCorrelatedColumnsInSubselectJoin => false;
}
}
......@@ -832,16 +832,6 @@ void PrepareFilterParameter()
HandleWithFragment(fromElement, with);
}
if (fromElement.Parent == null)
{
// Most likely means association join is used in invalid context
// I.e. in subquery: from EntityA a where exists (from EntityB join a.Assocation)
// Maybe we should throw exception instead
fromElement.FromClause.AddChild(fromElement);
if (fromElement.IsImplied)
fromElement.JoinSequence.SetUseThetaStyle(true);
}
}
if ( log.IsDebugEnabled() )
......@@ -930,7 +920,7 @@ private static string GetPropertyPath(DotNode dotNode, IASTNode alias)
return lhs.Path + "." + path;
}
IASTNode CreateFromElement(string path, IASTNode pathNode, IASTNode alias, IASTNode propertyFetch)
FromElement CreateFromElement(string path, IASTNode pathNode, IASTNode alias, IASTNode propertyFetch)
{
FromElement fromElement = _currentFromClause.AddFromElement(path, alias);
SetPropertyFetch(fromElement, propertyFetch, alias);
......
......@@ -266,10 +266,14 @@ fromElementList @init{
fromElement!
@init {
IASTNode fromElement = null;
FromElement fromElement = null;
}
// A simple class name, alias element.
: ^(RANGE p=path (a=ALIAS)? (pf=propertyFetch)? ) { fromElement = CreateFromElement($p.p, $p.tree, $a, $pf.tree); }
: ^(RANGE p=path (a=ALIAS)? (pf=propertyFetch)? )
{
fromElement = CreateFromElement($p.p, $p.tree, $a, $pf.tree);
fromElement.JoinSequence.SetUseThetaStyle(true);
}
-> {fromElement != null}? ^({fromElement})
->
| je=joinElement
......
......@@ -447,6 +447,11 @@ internal void FinishInit()
dependentElement.Parent.InsertChild(index, item);
}
}
if (_appendFromElements.Count > 0)
{
_fromElements[0].JoinSequence.SetUseThetaStyle(true);
}
_appendFromElements.Clear();
}
......
......@@ -707,12 +707,18 @@ public void SetOrigin(FromElement origin, bool manyToMany)
{
// HHH-276 : implied joins in a subselect where clause - The destination needs to be added
// to the destination's from clause.
FromClause.AddChild(this); // Not sure if this is will fix everything, but it works.
FromClause.AddChild(this);
// Generate correlated implied joins inside subquery implicitly
// As some dialects (MySql) do not support correlated columns to be used in subquery join ON clause
if (IsImplied)
{
JoinSequence.SetUseThetaStyle(true);
}
}
else
{
// Otherwise, the destination node was implied by the FROM clause and the FROM clause processor
// will automatically add it in the right place.
FromClause.AppendFromElement(this);
}
}
......
......@@ -122,10 +122,6 @@ public FromElement AddFromElement()
string tableAlias = correlatedSubselect ? fromElement.TableAlias : null;
//To properly generete subselect implicit join is required by SqlGenerator
if (fromElement.IsImplied)
fromElement.JoinSequence.SetUseThetaStyle(true);
// If the from element isn't in the same clause, create a new from element.
if (fromElement.FromClause != _fromClause)
{
......@@ -321,7 +317,7 @@ public FromElement CreateElementJoin(IQueryableCollection queryableCollection)
// 1) 'elem' is the "root from-element" in correlated subqueries
// 2) The DotNode.useThetaStyleImplicitJoins has been set to true
// and 'elem' represents an implicit join
if (elem.FromClause != elem.Origin.FromClause || DotNode.UseThetaStyleImplicitJoins)
if (DotNode.UseThetaStyleImplicitJoins)
{
// the "root from-element" in correlated subqueries do need this piece
elem.Type = HqlSqlWalker.FROM_FRAGMENT;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册