diff --git a/src/NHibernate.Test/Async/Hql/SubQueryTest.cs b/src/NHibernate.Test/Async/Hql/SubQueryTest.cs
new file mode 100644
index 00000000000..f88c5ed3629
--- /dev/null
+++ b/src/NHibernate.Test/Async/Hql/SubQueryTest.cs
@@ -0,0 +1,70 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Hql
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class SubQueryTestAsync : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.RootName);
+ rc.ManyToOne(x => x.Branch);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.BranchName);
+ rc.Bag(x => x.Leafs, cm => cm.Cascade(Mapping.ByCode.Cascade.All), x => x.OneToMany());
+ });
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.LeafName);
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect.SupportsScalarSubSelects;
+ }
+
+ [TestCase("SELECT CASE WHEN l.id IS NOT NULL THEN (SELECT COUNT(r.id) FROM Root r) ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE WHEN (SELECT COUNT(r.id) FROM Root r) > 1 THEN 1 ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE WHEN l.id > 1 THEN 1 ELSE (SELECT COUNT(r.id) FROM Root r) END FROM Leaf l")]
+ [TestCase("SELECT CASE (SELECT COUNT(r.id) FROM Root r) WHEN 1 THEN 1 ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE l.id WHEN (SELECT COUNT(r.id) FROM Root r) THEN 1 ELSE 0 END FROM Leaf l")]
+ public async Task TestSubQueryAsync(string query)
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ // Simple syntax check
+ await (session.CreateQuery(query).ListAsync());
+ await (transaction.CommitAsync());
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/FixtureByCode.cs
index d57b65898b5..0dac0e740f5 100644
--- a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/FixtureByCode.cs
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/FixtureByCode.cs
@@ -12,6 +12,7 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.Cfg.MappingSchema;
+using NHibernate.Exceptions;
using NHibernate.Mapping.ByCode;
using NHibernate.Type;
using NUnit.Framework;
@@ -124,8 +125,7 @@ protected async Task AreEqualAsync(
{
expectedResult = await (expectedQuery(session.Query()).ToListAsync(cancellationToken));
}
- catch (OperationCanceledException) { throw; }
- catch (Exception e)
+ catch (GenericADOException e)
{
Assert.Ignore($"Not currently supported query: {e}");
}
diff --git a/src/NHibernate.Test/Hql/Ast/SqlTranslationFixture.cs b/src/NHibernate.Test/Hql/Ast/SqlTranslationFixture.cs
index 2e2cd4cddff..ab734057d4f 100644
--- a/src/NHibernate.Test/Hql/Ast/SqlTranslationFixture.cs
+++ b/src/NHibernate.Test/Hql/Ast/SqlTranslationFixture.cs
@@ -23,6 +23,16 @@ public void CaseClauseWithMath()
Assert.DoesNotThrow(() => GetSql(queryWithoutParen));
}
+ [Test]
+ public void SimpleCaseClauseWithMath()
+ {
+ const string query = "from SimpleClass s where (case (cast(s.IntValue as long) * :pAValue) when (cast(s.IntValue as long) * :pAValue) then (cast(s.IntValue as long) * :pAValue) else 1 end) > 0";
+ Assert.DoesNotThrow(() => GetSql(query));
+
+ const string queryWithoutParen = "from SimpleClass s where (case cast(s.IntValue as long) * :pAValue when cast(s.IntValue as long) * :pAValue then cast(s.IntValue as long) * :pAValue else 1 end) > 0";
+ Assert.DoesNotThrow(() => GetSql(queryWithoutParen));
+ }
+
[Test]
public void Union()
{
diff --git a/src/NHibernate.Test/Hql/SubQueryTest.cs b/src/NHibernate.Test/Hql/SubQueryTest.cs
new file mode 100644
index 00000000000..caf665de998
--- /dev/null
+++ b/src/NHibernate.Test/Hql/SubQueryTest.cs
@@ -0,0 +1,59 @@
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Hql
+{
+ [TestFixture]
+ public class SubQueryTest : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.RootName);
+ rc.ManyToOne(x => x.Branch);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.BranchName);
+ rc.Bag(x => x.Leafs, cm => cm.Cascade(Mapping.ByCode.Cascade.All), x => x.OneToMany());
+ });
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Native));
+ rc.Property(x => x.LeafName);
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect.SupportsScalarSubSelects;
+ }
+
+ [TestCase("SELECT CASE WHEN l.id IS NOT NULL THEN (SELECT COUNT(r.id) FROM Root r) ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE WHEN (SELECT COUNT(r.id) FROM Root r) > 1 THEN 1 ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE WHEN l.id > 1 THEN 1 ELSE (SELECT COUNT(r.id) FROM Root r) END FROM Leaf l")]
+ [TestCase("SELECT CASE (SELECT COUNT(r.id) FROM Root r) WHEN 1 THEN 1 ELSE 0 END FROM Leaf l")]
+ [TestCase("SELECT CASE l.id WHEN (SELECT COUNT(r.id) FROM Root r) THEN 1 ELSE 0 END FROM Leaf l")]
+ public void TestSubQuery(string query)
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ // Simple syntax check
+ session.CreateQuery(query).List();
+ transaction.Commit();
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Hql/SubQueryTestEntities.cs b/src/NHibernate.Test/Hql/SubQueryTestEntities.cs
new file mode 100644
index 00000000000..2b431eeacf7
--- /dev/null
+++ b/src/NHibernate.Test/Hql/SubQueryTestEntities.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace NHibernate.Test.Hql
+{
+ public class Root
+ {
+ public virtual int Id { get; set; }
+
+ public virtual string RootName { get; set; }
+
+ public virtual Branch Branch { get; set; }
+ }
+
+ public class Branch
+ {
+ public virtual int Id { get; set; }
+
+ public virtual string BranchName { get; set; }
+
+ public virtual IList Leafs { get; set; }
+ }
+
+ public class Leaf
+ {
+ public virtual int Id { get; set; }
+
+ public virtual string LeafName { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH1879/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH1879/FixtureByCode.cs
index 3fe9211a449..6b491f59d00 100644
--- a/src/NHibernate.Test/NHSpecificTest/GH1879/FixtureByCode.cs
+++ b/src/NHibernate.Test/NHSpecificTest/GH1879/FixtureByCode.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.Cfg.MappingSchema;
+using NHibernate.Exceptions;
using NHibernate.Mapping.ByCode;
using NHibernate.Type;
using NUnit.Framework;
@@ -111,7 +112,7 @@ protected void AreEqual(
{
expectedResult = expectedQuery(session.Query()).ToList();
}
- catch (Exception e)
+ catch (GenericADOException e)
{
Assert.Ignore($"Not currently supported query: {e}");
}
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Hql.g b/src/NHibernate/Hql/Ast/ANTLR/Hql.g
index 2783936a19a..de0b52d8a7c 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Hql.g
+++ b/src/NHibernate/Hql/Ast/ANTLR/Hql.g
@@ -527,23 +527,31 @@ unaryExpression
;
caseExpression
- : CASE (whenClause)+ (elseClause)? END
- -> ^(CASE whenClause+ elseClause?)
- | CASE unaryExpression (altWhenClause)+ (elseClause)? END
- -> ^(CASE2 unaryExpression altWhenClause+ elseClause?)
+ : simpleCaseStatement
+ | searchedCaseStatement
;
-
-whenClause
- : (WHEN^ logicalExpression THEN! expression)
+
+simpleCaseStatement
+ : CASE expression (simpleCaseWhenClause)+ (elseClause)? END
+ -> ^(CASE2 expression simpleCaseWhenClause+ elseClause?)
;
-
-altWhenClause
- : (WHEN^ unaryExpression THEN! expression)
+
+simpleCaseWhenClause
+ : (WHEN^ expression THEN! expression)
;
elseClause
: (ELSE^ expression)
;
+
+searchedCaseStatement
+ : CASE (searchedCaseWhenClause)+ (elseClause)? END
+ -> ^(CASE searchedCaseWhenClause+ elseClause?)
+ ;
+
+searchedCaseWhenClause
+ : (WHEN^ logicalExpression THEN! expression)
+ ;
quantifiedExpression
: ( SOME^ | EXISTS^ | ALL^ | ANY^ )
diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.g b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.g
index 78825998019..befd1f3a2e5 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.g
+++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.g
@@ -432,8 +432,28 @@ arithmeticExpr
;
caseExpr
- : ^(CASE { _inCase = true; } (^(WHEN logicalExpr expr))+ (^(ELSE expr))?) { _inCase = false; }
- | ^(CASE2 { _inCase = true; } expr (^(WHEN expr expr))+ (^(ELSE expr))?) { _inCase = false; }
+ : simpleCaseExpression
+ | searchedCaseExpression
+ ;
+
+simpleCaseExpression
+ : ^(CASE2 {_inCase=true;} exprOrSubquery (simpleCaseWhenClause)+ (elseClause)?) {_inCase=false;}
+ ;
+
+simpleCaseWhenClause
+ : ^(WHEN exprOrSubquery exprOrSubquery)
+ ;
+
+elseClause
+ : ^(ELSE exprOrSubquery)
+ ;
+
+searchedCaseExpression
+ : ^(CASE {_inCase = true;} (searchedCaseWhenClause)+ (elseClause)?) {_inCase = false;}
+ ;
+
+searchedCaseWhenClause
+ : ^(WHEN logicalExpr exprOrSubquery)
;
//TODO: I don't think we need this anymore .. how is it different to
diff --git a/src/NHibernate/Linq/ReWriters/ConditionalQueryReferenceExpander.cs b/src/NHibernate/Linq/ReWriters/ConditionalQueryReferenceExpander.cs
index b83be7077b4..c67039b1863 100644
--- a/src/NHibernate/Linq/ReWriters/ConditionalQueryReferenceExpander.cs
+++ b/src/NHibernate/Linq/ReWriters/ConditionalQueryReferenceExpander.cs
@@ -31,6 +31,14 @@ public override void VisitSelectClause(SelectClause selectClause, QueryModel que
_expander.Transform(selectClause);
}
+ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
+ {
+ if (fromClause.FromExpression is SubQueryExpression subqueryExpression)
+ {
+ VisitQueryModel(subqueryExpression.QueryModel);
+ }
+ }
+
public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index)
{
_expander.Transform(ordering);
diff --git a/src/NHibernate/Linq/ReWriters/SubQueryConditionalExpander.cs b/src/NHibernate/Linq/ReWriters/SubQueryConditionalExpander.cs
index 3c6d3a4231f..5319018b021 100644
--- a/src/NHibernate/Linq/ReWriters/SubQueryConditionalExpander.cs
+++ b/src/NHibernate/Linq/ReWriters/SubQueryConditionalExpander.cs
@@ -24,6 +24,14 @@ public override void VisitSelectClause(SelectClause selectClause, QueryModel que
_expander.Transform(selectClause);
}
+ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
+ {
+ if (fromClause.FromExpression is SubQueryExpression subqueryExpression)
+ {
+ VisitQueryModel(subqueryExpression.QueryModel);
+ }
+ }
+
public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index)
{
_expander.Transform(ordering);