From 93cd8484b204d97e7c3dcba75ac3b33000628c5c Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 15 Mar 2019 20:25:00 +0100 Subject: [PATCH 1/6] Reduce cast usage for COUNT aggregate and add support for Mssql count_big --- .../Async/Linq/ByMethod/CountTests.cs | 46 +++++++++++++++++++ .../Async/QueryTest/CountFixture.cs | 4 +- .../Hql/SimpleFunctionsTest.cs | 3 +- .../Linq/ByMethod/CountTests.cs | 46 +++++++++++++++++++ src/NHibernate.Test/QueryTest/CountFixture.cs | 17 ++++++- src/NHibernate/Dialect/Dialect.cs | 1 + .../Function/ClassicAggregateFunction.cs | 15 +++++- .../Dialect/Function/ClassicCountFunction.cs | 4 +- .../Dialect/Function/ISQLAggregateFunction.cs | 22 +++++++++ src/NHibernate/Dialect/MsSql2000Dialect.cs | 13 +++++- src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs | 7 +++ src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g | 4 +- .../Hql/Ast/ANTLR/Tree/AggregateNode.cs | 3 ++ .../Hql/Ast/ANTLR/Tree/CountNode.cs | 7 +-- src/NHibernate/Hql/Ast/HqlTreeBuilder.cs | 5 ++ src/NHibernate/Hql/Ast/HqlTreeNode.cs | 13 ++++++ .../Visitors/HqlGeneratorExpressionVisitor.cs | 11 ++++- 17 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs diff --git a/src/NHibernate.Test/Async/Linq/ByMethod/CountTests.cs b/src/NHibernate.Test/Async/Linq/ByMethod/CountTests.cs index 8dfdc304aae..5581badf6c8 100644 --- a/src/NHibernate.Test/Async/Linq/ByMethod/CountTests.cs +++ b/src/NHibernate.Test/Async/Linq/ByMethod/CountTests.cs @@ -11,6 +11,7 @@ using System; using System.Linq; using NHibernate.Cfg; +using NHibernate.Dialect; using NUnit.Framework; using NHibernate.Linq; @@ -110,5 +111,50 @@ into temp Assert.That(result.Count, Is.EqualTo(77)); } + + [Test] + public async Task CheckSqlFunctionNameLongCountAsync() + { + var name = Dialect is MsSql2000Dialect ? "count_big" : "count"; + using (var sqlLog = new SqlLogSpy()) + { + var result = await (db.Orders.LongCountAsync()); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Contain($"{name}(")); + } + } + + [Test] + public async Task CheckSqlFunctionNameForCountAsync() + { + using (var sqlLog = new SqlLogSpy()) + { + var result = await (db.Orders.CountAsync()); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Contain("count(")); + } + } + + [Test] + public async Task CheckMssqlCountCastAsync() + { + if (!(Dialect is MsSql2000Dialect)) + { + Assert.Ignore(); + } + + using (var sqlLog = new SqlLogSpy()) + { + var result = await (db.Orders.CountAsync()); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Not.Contain("cast(")); + } + } } } diff --git a/src/NHibernate.Test/Async/QueryTest/CountFixture.cs b/src/NHibernate.Test/Async/QueryTest/CountFixture.cs index 75e57dba100..7b887ddbba4 100644 --- a/src/NHibernate.Test/Async/QueryTest/CountFixture.cs +++ b/src/NHibernate.Test/Async/QueryTest/CountFixture.cs @@ -12,6 +12,8 @@ using NHibernate.Cfg; using NHibernate.Dialect.Function; using NHibernate.DomainModel; +using NHibernate.Engine; +using NHibernate.Type; using NUnit.Framework; using Environment=NHibernate.Cfg.Environment; @@ -55,4 +57,4 @@ public async Task OverriddenAsync() await (sf.CloseAsync()); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs b/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs index 855ecfc7f2d..8ea0267aab4 100644 --- a/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs +++ b/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs @@ -135,7 +135,8 @@ public void ClassicSum() Assert.Throws(() => csf.Render(args, factoryImpl)); } - [Test] + // Since v5.3 + [Test, Obsolete] public void ClassicCount() { //ANSI-SQL92 definition diff --git a/src/NHibernate.Test/Linq/ByMethod/CountTests.cs b/src/NHibernate.Test/Linq/ByMethod/CountTests.cs index 3bb93c7a083..7ef2d7dbbe6 100644 --- a/src/NHibernate.Test/Linq/ByMethod/CountTests.cs +++ b/src/NHibernate.Test/Linq/ByMethod/CountTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using NHibernate.Cfg; +using NHibernate.Dialect; using NUnit.Framework; namespace NHibernate.Test.Linq.ByMethod @@ -98,5 +99,50 @@ into temp Assert.That(result.Count, Is.EqualTo(77)); } + + [Test] + public void CheckSqlFunctionNameLongCount() + { + var name = Dialect is MsSql2000Dialect ? "count_big" : "count"; + using (var sqlLog = new SqlLogSpy()) + { + var result = db.Orders.LongCount(); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Contain($"{name}(")); + } + } + + [Test] + public void CheckSqlFunctionNameForCount() + { + using (var sqlLog = new SqlLogSpy()) + { + var result = db.Orders.Count(); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Contain("count(")); + } + } + + [Test] + public void CheckMssqlCountCast() + { + if (!(Dialect is MsSql2000Dialect)) + { + Assert.Ignore(); + } + + using (var sqlLog = new SqlLogSpy()) + { + var result = db.Orders.Count(); + Assert.That(result, Is.EqualTo(830)); + + var log = sqlLog.GetWholeLog(); + Assert.That(log, Does.Not.Contain("cast(")); + } + } } } diff --git a/src/NHibernate.Test/QueryTest/CountFixture.cs b/src/NHibernate.Test/QueryTest/CountFixture.cs index d696122177e..ec7e790b5af 100644 --- a/src/NHibernate.Test/QueryTest/CountFixture.cs +++ b/src/NHibernate.Test/QueryTest/CountFixture.cs @@ -2,6 +2,8 @@ using NHibernate.Cfg; using NHibernate.Dialect.Function; using NHibernate.DomainModel; +using NHibernate.Engine; +using NHibernate.Type; using NUnit.Framework; using Environment=NHibernate.Cfg.Environment; @@ -44,4 +46,17 @@ public void Overridden() sf.Close(); } } -} \ No newline at end of file + + [Serializable] + internal class ClassicCountFunction : ClassicAggregateFunction + { + public ClassicCountFunction() : base("count", true) + { + } + + public override IType ReturnType(IType columnType, IMapping mapping) + { + return NHibernateUtil.Int32; + } + } +} diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs index af1995dca06..53c30b55175 100644 --- a/src/NHibernate/Dialect/Dialect.cs +++ b/src/NHibernate/Dialect/Dialect.cs @@ -55,6 +55,7 @@ public abstract partial class Dialect static Dialect() { StandardAggregateFunctions["count"] = new CountQueryFunctionInfo(); + StandardAggregateFunctions["count_big"] = new CountQueryFunctionInfo(); StandardAggregateFunctions["avg"] = new AvgQueryFunctionInfo(); StandardAggregateFunctions["max"] = new ClassicAggregateFunction("max", false); StandardAggregateFunctions["min"] = new ClassicAggregateFunction("min", false); diff --git a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs index 299605adb03..8e256642093 100644 --- a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs +++ b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs @@ -9,7 +9,7 @@ namespace NHibernate.Dialect.Function { [Serializable] - public class ClassicAggregateFunction : ISQLFunction, IFunctionGrammar + public class ClassicAggregateFunction : ISQLFunction, IFunctionGrammar, ISQLAggregateFunction { private IType returnType = null; private readonly string name; @@ -110,5 +110,18 @@ bool IFunctionGrammar.IsKnownArgument(string token) } #endregion + + #region ISQLAggregateFunction Members + + /// + public string FunctionName => name; + + /// + public virtual IType GetActualReturnType(IType argumentType, IMapping mapping) + { + return ReturnType(argumentType, mapping); + } + + #endregion } } diff --git a/src/NHibernate/Dialect/Function/ClassicCountFunction.cs b/src/NHibernate/Dialect/Function/ClassicCountFunction.cs index 821146519e9..c3aba6841ee 100644 --- a/src/NHibernate/Dialect/Function/ClassicCountFunction.cs +++ b/src/NHibernate/Dialect/Function/ClassicCountFunction.cs @@ -7,6 +7,8 @@ namespace NHibernate.Dialect.Function /// /// Classic COUNT sqlfunction that return types as it was done in Hibernate 3.1 /// + // Since v5.3 + [Obsolete("This class has no more usages in NHibernate and will be removed in a future version.")] [Serializable] public class ClassicCountFunction : ClassicAggregateFunction { @@ -19,4 +21,4 @@ public override IType ReturnType(IType columnType, IMapping mapping) return NHibernateUtil.Int32; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs b/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs new file mode 100644 index 00000000000..86f62bbca99 --- /dev/null +++ b/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs @@ -0,0 +1,22 @@ +using NHibernate.Engine; +using NHibernate.Type; + +namespace NHibernate.Dialect.Function +{ + /// + internal interface ISQLAggregateFunction : ISQLFunction + { + /// + /// The name of the aggregate function. + /// + string FunctionName { get; } + + /// + /// Get the type that will be effectively returned by the underlying database. + /// + /// The type of the first argument + /// The mapping for retrieving the argument sql types. + /// + IType GetActualReturnType(IType argumentType, IMapping mapping); + } +} diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs index 7acb3cb9b4f..d78a4be265d 100644 --- a/src/NHibernate/Dialect/MsSql2000Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs @@ -286,7 +286,8 @@ protected virtual void RegisterKeywords() protected virtual void RegisterFunctions() { - RegisterFunction("count", new CountBigQueryFunction()); + RegisterFunction("count", new CountQueryFunction()); + RegisterFunction("count_big", new CountBigQueryFunction()); RegisterFunction("abs", new StandardSQLFunction("abs")); RegisterFunction("absval", new StandardSQLFunction("absval")); @@ -712,6 +713,16 @@ public override IType ReturnType(IType columnType, IMapping mapping) } } + [Serializable] + private class CountQueryFunction : CountQueryFunctionInfo + { + /// + public override IType GetActualReturnType(IType columnType, IMapping mapping) + { + return NHibernateUtil.Int32; + } + } + public override bool SupportsCircularCascadeDeleteConstraints { get diff --git a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs index 4f6f902891d..f13ec5259d1 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs @@ -310,6 +310,13 @@ private void EndFunctionTemplate(IASTNode m) } } + private void OutAggregateFunctionName(IASTNode m) + { + var aggregateNode = (AggregateNode) m; + var template = aggregateNode.SqlFunction; + Out(template == null ? aggregateNode.Text : template.FunctionName); + } + private void CommaBetweenParameters(String comma) { writer.CommaBetweenParameters(comma); diff --git a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g index 73d04c792ec..21cb3c7521e 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g +++ b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.g @@ -150,7 +150,7 @@ selectExpr ; count - : ^(COUNT { Out("count("); } ( distinctOrAll ) ? countExpr { Out(")"); } ) + : ^(c=COUNT { OutAggregateFunctionName(c); Out("("); } ( distinctOrAll ) ? countExpr { Out(")"); } ) ; distinctOrAll @@ -344,7 +344,7 @@ caseExpr ; aggregate - : ^(a=AGGREGATE { Out(a); Out("("); } expr { Out(")"); } ) + : ^(a=AGGREGATE { OutAggregateFunctionName(a); Out("("); } expr { Out(")"); } ) ; diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs index d7dc2f9a0f0..fa0b310500e 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs @@ -1,5 +1,6 @@ using System; using Antlr.Runtime; +using NHibernate.Dialect.Function; using NHibernate.Type; using NHibernate.Hql.Ast.ANTLR.Util; @@ -19,6 +20,8 @@ public AggregateNode(IToken token) { } + internal ISQLAggregateFunction SqlFunction => SessionFactoryHelper.FindSQLFunction(Text) as ISQLAggregateFunction; + public override IType DataType { get diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/CountNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/CountNode.cs index 88ec9cc22fd..ef6d6272043 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/CountNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/CountNode.cs @@ -1,4 +1,5 @@ using Antlr.Runtime; +using NHibernate.Dialect.Function; using NHibernate.Hql.Ast.ANTLR.Util; using NHibernate.Type; @@ -9,7 +10,7 @@ namespace NHibernate.Hql.Ast.ANTLR.Tree /// Author: josh /// Ported by: Steve Strong /// - class CountNode : AbstractSelectExpression, ISelectExpression + class CountNode : AggregateNode, ISelectExpression { public CountNode(IToken token) : base(token) { @@ -26,9 +27,5 @@ public override IType DataType base.DataType = value; } } - public override void SetScalarColumnText(int i) - { - ColumnHelper.GenerateSingleScalarColumn(ASTFactory, this, i); - } } } diff --git a/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs b/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs index bb208295afc..dd59f2ff0f0 100755 --- a/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs +++ b/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs @@ -307,6 +307,11 @@ public HqlCount Count(HqlExpression child) return new HqlCount(_factory, child); } + public HqlCountBig CountBig(HqlExpression child) + { + return new HqlCountBig(_factory, child); + } + public HqlRowStar RowStar() { return new HqlRowStar(_factory); diff --git a/src/NHibernate/Hql/Ast/HqlTreeNode.cs b/src/NHibernate/Hql/Ast/HqlTreeNode.cs index 160739d7f16..8967174bde3 100755 --- a/src/NHibernate/Hql/Ast/HqlTreeNode.cs +++ b/src/NHibernate/Hql/Ast/HqlTreeNode.cs @@ -697,6 +697,19 @@ public HqlCount(IASTFactory factory, HqlExpression child) } } + public class HqlCountBig : HqlExpression + { + public HqlCountBig(IASTFactory factory) + : base(HqlSqlWalker.COUNT, "count_big", factory) + { + } + + public HqlCountBig(IASTFactory factory, HqlExpression child) + : base(HqlSqlWalker.COUNT, "count_big", factory, child) + { + } + } + public class HqlAs : HqlExpression { public HqlAs(IASTFactory factory, HqlExpression expression, System.Type type) diff --git a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs index 224af655a24..dbce1abeb4d 100644 --- a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs +++ b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs @@ -257,7 +257,16 @@ protected HqlTreeNode VisitNhAverage(NhAverageExpression expression) protected HqlTreeNode VisitNhCount(NhCountExpression expression) { - return _hqlTreeBuilder.Cast(_hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()), expression.Type); + if (expression is NhLongCountExpression) + { + return IsCastRequired(expression.Type, "count_big", out _) + ? (HqlTreeNode) _hqlTreeBuilder.Cast(_hqlTreeBuilder.CountBig(VisitExpression(expression.Expression).AsExpression()), expression.Type) + : _hqlTreeBuilder.TransparentCast(_hqlTreeBuilder.CountBig(VisitExpression(expression.Expression).AsExpression()), expression.Type); + } + + return IsCastRequired(expression.Type, "count", out _) + ? (HqlTreeNode) _hqlTreeBuilder.Cast(_hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()), expression.Type) + : _hqlTreeBuilder.TransparentCast(_hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()), expression.Type); } protected HqlTreeNode VisitNhMin(NhMinExpression expression) From 3a8dc8c9dc08c8b1a566c30e9d894e4f804cbe61 Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 21 Feb 2020 21:56:10 +0100 Subject: [PATCH 2/6] Rename ISQLFunctionExtended interface to ISQLFunctionExtended --- .../Async/QueryTest/CountFixture.cs | 4 +- .../Hql/SimpleFunctionsTest.cs | 3 +- src/NHibernate.Test/QueryTest/CountFixture.cs | 17 +------ .../Function/ClassicAggregateFunction.cs | 28 +++++------- .../Dialect/Function/ClassicCountFunction.cs | 4 +- .../Dialect/Function/ISQLAggregateFunction.cs | 22 --------- .../Dialect/Function/ISQLFunction.cs | 45 +++++++++++++++++++ .../Dialect/Function/ISQLFunctionExtended.cs | 27 +++++++++++ src/NHibernate/Dialect/MsSql2000Dialect.cs | 5 +-- src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs | 3 +- .../Hql/Ast/ANTLR/Tree/AggregateNode.cs | 2 +- .../Visitors/HqlGeneratorExpressionVisitor.cs | 35 +++++++-------- src/NHibernate/Util/ExpressionsHelper.cs | 23 ++++++++++ 13 files changed, 131 insertions(+), 87 deletions(-) delete mode 100644 src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs create mode 100644 src/NHibernate/Dialect/Function/ISQLFunctionExtended.cs diff --git a/src/NHibernate.Test/Async/QueryTest/CountFixture.cs b/src/NHibernate.Test/Async/QueryTest/CountFixture.cs index 7b887ddbba4..75e57dba100 100644 --- a/src/NHibernate.Test/Async/QueryTest/CountFixture.cs +++ b/src/NHibernate.Test/Async/QueryTest/CountFixture.cs @@ -12,8 +12,6 @@ using NHibernate.Cfg; using NHibernate.Dialect.Function; using NHibernate.DomainModel; -using NHibernate.Engine; -using NHibernate.Type; using NUnit.Framework; using Environment=NHibernate.Cfg.Environment; @@ -57,4 +55,4 @@ public async Task OverriddenAsync() await (sf.CloseAsync()); } } -} +} \ No newline at end of file diff --git a/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs b/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs index 8ea0267aab4..855ecfc7f2d 100644 --- a/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs +++ b/src/NHibernate.Test/Hql/SimpleFunctionsTest.cs @@ -135,8 +135,7 @@ public void ClassicSum() Assert.Throws(() => csf.Render(args, factoryImpl)); } - // Since v5.3 - [Test, Obsolete] + [Test] public void ClassicCount() { //ANSI-SQL92 definition diff --git a/src/NHibernate.Test/QueryTest/CountFixture.cs b/src/NHibernate.Test/QueryTest/CountFixture.cs index ec7e790b5af..d696122177e 100644 --- a/src/NHibernate.Test/QueryTest/CountFixture.cs +++ b/src/NHibernate.Test/QueryTest/CountFixture.cs @@ -2,8 +2,6 @@ using NHibernate.Cfg; using NHibernate.Dialect.Function; using NHibernate.DomainModel; -using NHibernate.Engine; -using NHibernate.Type; using NUnit.Framework; using Environment=NHibernate.Cfg.Environment; @@ -46,17 +44,4 @@ public void Overridden() sf.Close(); } } - - [Serializable] - internal class ClassicCountFunction : ClassicAggregateFunction - { - public ClassicCountFunction() : base("count", true) - { - } - - public override IType ReturnType(IType columnType, IMapping mapping) - { - return NHibernateUtil.Int32; - } - } -} +} \ No newline at end of file diff --git a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs index 8e256642093..e0b78f1e1e2 100644 --- a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs +++ b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs @@ -1,15 +1,15 @@ using System; using System.Collections; -using System.Text; +using System.Collections.Generic; +using System.Linq; using NHibernate.Engine; using NHibernate.SqlCommand; using NHibernate.Type; -using NHibernate.Util; namespace NHibernate.Dialect.Function { [Serializable] - public class ClassicAggregateFunction : ISQLFunction, IFunctionGrammar, ISQLAggregateFunction + public class ClassicAggregateFunction : ISQLFunction, IFunctionGrammar, ISQLFunctionExtended { private IType returnType = null; private readonly string name; @@ -45,6 +45,15 @@ public virtual IType ReturnType(IType columnType, IMapping mapping) return returnType ?? columnType; } + /// + public virtual IType GetEffectiveReturnType(IEnumerable argumentTypes, IMapping mapping, bool throwOnError) + { + return ReturnType(argumentTypes.FirstOrDefault(), mapping); + } + + /// + public string FunctionName => name; + public bool HasArguments { get { return true; } @@ -110,18 +119,5 @@ bool IFunctionGrammar.IsKnownArgument(string token) } #endregion - - #region ISQLAggregateFunction Members - - /// - public string FunctionName => name; - - /// - public virtual IType GetActualReturnType(IType argumentType, IMapping mapping) - { - return ReturnType(argumentType, mapping); - } - - #endregion } } diff --git a/src/NHibernate/Dialect/Function/ClassicCountFunction.cs b/src/NHibernate/Dialect/Function/ClassicCountFunction.cs index c3aba6841ee..821146519e9 100644 --- a/src/NHibernate/Dialect/Function/ClassicCountFunction.cs +++ b/src/NHibernate/Dialect/Function/ClassicCountFunction.cs @@ -7,8 +7,6 @@ namespace NHibernate.Dialect.Function /// /// Classic COUNT sqlfunction that return types as it was done in Hibernate 3.1 /// - // Since v5.3 - [Obsolete("This class has no more usages in NHibernate and will be removed in a future version.")] [Serializable] public class ClassicCountFunction : ClassicAggregateFunction { @@ -21,4 +19,4 @@ public override IType ReturnType(IType columnType, IMapping mapping) return NHibernateUtil.Int32; } } -} +} \ No newline at end of file diff --git a/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs b/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs deleted file mode 100644 index 86f62bbca99..00000000000 --- a/src/NHibernate/Dialect/Function/ISQLAggregateFunction.cs +++ /dev/null @@ -1,22 +0,0 @@ -using NHibernate.Engine; -using NHibernate.Type; - -namespace NHibernate.Dialect.Function -{ - /// - internal interface ISQLAggregateFunction : ISQLFunction - { - /// - /// The name of the aggregate function. - /// - string FunctionName { get; } - - /// - /// Get the type that will be effectively returned by the underlying database. - /// - /// The type of the first argument - /// The mapping for retrieving the argument sql types. - /// - IType GetActualReturnType(IType argumentType, IMapping mapping); - } -} diff --git a/src/NHibernate/Dialect/Function/ISQLFunction.cs b/src/NHibernate/Dialect/Function/ISQLFunction.cs index 4433e5dc63c..09b974ec9d8 100644 --- a/src/NHibernate/Dialect/Function/ISQLFunction.cs +++ b/src/NHibernate/Dialect/Function/ISQLFunction.cs @@ -1,4 +1,6 @@ using System.Collections; +using System.Collections.Generic; +using System.Linq; using NHibernate.Engine; using NHibernate.SqlCommand; using NHibernate.Type; @@ -41,4 +43,47 @@ public interface ISQLFunction /// SQL fragment for the function. SqlString Render(IList args, ISessionFactoryImplementor factory); } + + // 6.0 TODO: Remove + internal static class SQLFunctionExtensions + { + /// + /// Get the type that will be effectively returned by the underlying database. + /// + /// The sql function. + /// The types of arguments. + /// The mapping for retrieving the argument sql types. + /// Whether to throw when the number of arguments is invalid or they are not supported. + /// The type returned by the underlying database or when the number of arguments + /// is invalid or they are not supported. + /// When is set to and the + /// number of arguments is invalid or they are not supported. + public static IType GetEffectiveReturnType( + this ISQLFunction sqlFunction, + IEnumerable argumentTypes, + IMapping mapping, + bool throwOnError) + { + if (!(sqlFunction is ISQLFunctionExtended extendedSqlFunction)) + { + try + { +#pragma warning disable 618 + return sqlFunction.ReturnType(argumentTypes.FirstOrDefault(), mapping); +#pragma warning restore 618 + } + catch (QueryException) + { + if (throwOnError) + { + throw; + } + + return null; + } + } + + return extendedSqlFunction.GetEffectiveReturnType(argumentTypes, mapping, throwOnError); + } + } } diff --git a/src/NHibernate/Dialect/Function/ISQLFunctionExtended.cs b/src/NHibernate/Dialect/Function/ISQLFunctionExtended.cs new file mode 100644 index 00000000000..e2db4747198 --- /dev/null +++ b/src/NHibernate/Dialect/Function/ISQLFunctionExtended.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using NHibernate.Engine; +using NHibernate.Type; + +namespace NHibernate.Dialect.Function +{ + // 6.0 TODO: Merge into ISQLFunction + internal interface ISQLFunctionExtended : ISQLFunction + { + /// + /// The function name or when multiple functions/operators/statements are used. + /// + string FunctionName { get; } + + /// + /// Get the type that will be effectively returned by the underlying database. + /// + /// The types of arguments. + /// The mapping for retrieving the argument sql types. + /// Whether to throw when the number of arguments is invalid or they are not supported. + /// The type returned by the underlying database or when the number of arguments + /// is invalid or they are not supported. + /// When is set to and the + /// number of arguments is invalid or they are not supported. + IType GetEffectiveReturnType(IEnumerable argumentTypes, IMapping mapping, bool throwOnError); + } +} diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs index d78a4be265d..90fa66fabc4 100644 --- a/src/NHibernate/Dialect/MsSql2000Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs @@ -707,7 +707,7 @@ protected class CountBigQueryFunction : ClassicAggregateFunction { public CountBigQueryFunction() : base("count_big", true) { } - public override IType ReturnType(IType columnType, IMapping mapping) + public override IType GetEffectiveReturnType(IEnumerable argumentTypes, IMapping mapping, bool throwOnError) { return NHibernateUtil.Int64; } @@ -716,8 +716,7 @@ public override IType ReturnType(IType columnType, IMapping mapping) [Serializable] private class CountQueryFunction : CountQueryFunctionInfo { - /// - public override IType GetActualReturnType(IType columnType, IMapping mapping) + public override IType GetEffectiveReturnType(IEnumerable argumentTypes, IMapping mapping, bool throwOnError) { return NHibernateUtil.Int32; } diff --git a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs index f13ec5259d1..87bcc2998a9 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs @@ -313,8 +313,7 @@ private void EndFunctionTemplate(IASTNode m) private void OutAggregateFunctionName(IASTNode m) { var aggregateNode = (AggregateNode) m; - var template = aggregateNode.SqlFunction; - Out(template == null ? aggregateNode.Text : template.FunctionName); + Out(aggregateNode.SqlFunction?.FunctionName ?? aggregateNode.Text); } private void CommaBetweenParameters(String comma) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs index fa0b310500e..517f82b4aea 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs @@ -20,7 +20,7 @@ public AggregateNode(IToken token) { } - internal ISQLAggregateFunction SqlFunction => SessionFactoryHelper.FindSQLFunction(Text) as ISQLAggregateFunction; + internal ISQLFunctionExtended SqlFunction => SessionFactoryHelper.FindSQLFunction(Text) as ISQLFunctionExtended; public override IType DataType { diff --git a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs index dbce1abeb4d..cd9cd49eadb 100644 --- a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs +++ b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; +using NHibernate.Dialect.Function; using NHibernate.Engine.Query; using NHibernate.Hql.Ast; using NHibernate.Hql.Ast.ANTLR; @@ -257,16 +258,22 @@ protected HqlTreeNode VisitNhAverage(NhAverageExpression expression) protected HqlTreeNode VisitNhCount(NhCountExpression expression) { + string functionName; + HqlExpression countHqlExpression; if (expression is NhLongCountExpression) { - return IsCastRequired(expression.Type, "count_big", out _) - ? (HqlTreeNode) _hqlTreeBuilder.Cast(_hqlTreeBuilder.CountBig(VisitExpression(expression.Expression).AsExpression()), expression.Type) - : _hqlTreeBuilder.TransparentCast(_hqlTreeBuilder.CountBig(VisitExpression(expression.Expression).AsExpression()), expression.Type); + functionName = "count_big"; + countHqlExpression = _hqlTreeBuilder.CountBig(VisitExpression(expression.Expression).AsExpression()); + } + else + { + functionName = "count"; + countHqlExpression = _hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()); } - return IsCastRequired(expression.Type, "count", out _) - ? (HqlTreeNode) _hqlTreeBuilder.Cast(_hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()), expression.Type) - : _hqlTreeBuilder.TransparentCast(_hqlTreeBuilder.Count(VisitExpression(expression.Expression).AsExpression()), expression.Type); + return IsCastRequired(functionName, expression.Expression, expression.Type) + ? (HqlTreeNode) _hqlTreeBuilder.Cast(countHqlExpression, expression.Type) + : _hqlTreeBuilder.TransparentCast(countHqlExpression, expression.Type); } protected HqlTreeNode VisitNhMin(NhMinExpression expression) @@ -604,7 +611,7 @@ private bool IsCastRequired(Expression expression, System.Type toType, out bool { existType = false; return toType != typeof(object) && - IsCastRequired(GetType(expression), TypeFactory.GetDefaultTypeFor(toType), out existType); + IsCastRequired(ExpressionsHelper.GetType(_parameters, expression), TypeFactory.GetDefaultTypeFor(toType), out existType); } private bool IsCastRequired(IType type, IType toType, out bool existType) @@ -648,7 +655,7 @@ private bool IsCastRequired(IType type, IType toType, out bool existType) private bool IsCastRequired(string sqlFunctionName, Expression argumentExpression, System.Type returnType) { - var argumentType = GetType(argumentExpression); + var argumentType = ExpressionsHelper.GetType(_parameters, argumentExpression); if (argumentType == null || returnType == typeof(object)) { return false; @@ -666,18 +673,8 @@ private bool IsCastRequired(string sqlFunctionName, Expression argumentExpressio return true; // Fallback to the old behavior } - var fnReturnType = sqlFunction.ReturnType(argumentType, _parameters.SessionFactory); + var fnReturnType = sqlFunction.GetEffectiveReturnType(new[] {argumentType}, _parameters.SessionFactory, false); return fnReturnType == null || IsCastRequired(fnReturnType, returnNhType, out _); } - - private IType GetType(Expression expression) - { - // Try to get the mapped type for the member as it may be a non default one - return expression.Type == typeof(object) - ? null - : (ExpressionsHelper.TryGetMappedType(_parameters.SessionFactory, expression, out var type, out _, out _, out _) - ? type - : TypeFactory.GetDefaultTypeFor(expression.Type)); - } } } diff --git a/src/NHibernate/Util/ExpressionsHelper.cs b/src/NHibernate/Util/ExpressionsHelper.cs index 86fb8d04bd3..376a42ccda0 100644 --- a/src/NHibernate/Util/ExpressionsHelper.cs +++ b/src/NHibernate/Util/ExpressionsHelper.cs @@ -28,6 +28,29 @@ public static MemberInfo DecodeMemberAccessExpression(Expressi return ((MemberExpression)expression.Body).Member; } + /// + /// Get the mapped type for the given expression. + /// + /// The query parameters. + /// The expression. + /// The mapped type of the expression or when the mapped type was not + /// found and the type is . + internal static IType GetType(VisitorParameters parameters, Expression expression) + { + if (expression is ConstantExpression constantExpression && + parameters.ConstantToParameterMap.TryGetValue(constantExpression, out var param)) + { + return param.Type; + } + + if (TryGetMappedType(parameters.SessionFactory, expression, out var type, out _, out _, out _)) + { + return type; + } + + return expression.Type == typeof(object) ? null : TypeFactory.HeuristicType(expression.Type); + } + /// /// Try to get the mapped nullability from the given expression. /// From df065e14590a4650d706d0eda14930f5cdbb811c Mon Sep 17 00:00:00 2001 From: maca88 Date: Sat, 22 Feb 2020 15:05:11 +0100 Subject: [PATCH 3/6] Fix CountBigQueryFunction ReturnType method --- src/NHibernate/Dialect/MsSql2000Dialect.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs index 90fa66fabc4..35c760e85b3 100644 --- a/src/NHibernate/Dialect/MsSql2000Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs @@ -705,12 +705,7 @@ protected virtual string GetSelectExistingObject(string catalog, string schema, [Serializable] protected class CountBigQueryFunction : ClassicAggregateFunction { - public CountBigQueryFunction() : base("count_big", true) { } - - public override IType GetEffectiveReturnType(IEnumerable argumentTypes, IMapping mapping, bool throwOnError) - { - return NHibernateUtil.Int64; - } + public CountBigQueryFunction() : base("count_big", true, NHibernateUtil.Int64) { } } [Serializable] From 5f5ad60e64f7b56d3a0ea0f05de45f0c99a94c98 Mon Sep 17 00:00:00 2001 From: maca88 Date: Mon, 30 Mar 2020 18:18:21 +0200 Subject: [PATCH 4/6] Remove unneeded pragma warning --- src/NHibernate/Dialect/Function/ISQLFunction.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NHibernate/Dialect/Function/ISQLFunction.cs b/src/NHibernate/Dialect/Function/ISQLFunction.cs index 09b974ec9d8..5302625a01e 100644 --- a/src/NHibernate/Dialect/Function/ISQLFunction.cs +++ b/src/NHibernate/Dialect/Function/ISQLFunction.cs @@ -68,9 +68,7 @@ public static IType GetEffectiveReturnType( { try { -#pragma warning disable 618 return sqlFunction.ReturnType(argumentTypes.FirstOrDefault(), mapping); -#pragma warning restore 618 } catch (QueryException) { From 83715eea3b75622d5c3ebd421a86d36ad3cb6535 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Wed, 1 Apr 2020 09:14:53 +1300 Subject: [PATCH 5/6] Encapsulate implementation details --- src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs | 2 +- src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs index 87bcc2998a9..7d1ab9a0420 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs @@ -313,7 +313,7 @@ private void EndFunctionTemplate(IASTNode m) private void OutAggregateFunctionName(IASTNode m) { var aggregateNode = (AggregateNode) m; - Out(aggregateNode.SqlFunction?.FunctionName ?? aggregateNode.Text); + Out(aggregateNode.SqlFunctionName()); } private void CommaBetweenParameters(String comma) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs index 517f82b4aea..a88701a94da 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs @@ -34,9 +34,20 @@ public override IType DataType base.DataType = value; } } + public override void SetScalarColumnText(int i) { ColumnHelper.GenerateSingleScalarColumn(ASTFactory, this, i); } + + public string SqlFunctionName() + { + if (SessionFactoryHelper.FindSQLFunction(Text) is ISQLFunctionExtended sqlFunction) + { + return sqlFunction.FunctionName; + } + + return Text; + } } } From bc4b6e5e32609d5d1e6baab5d554f7ced523d822 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Wed, 1 Apr 2020 09:16:13 +1300 Subject: [PATCH 6/6] Code cleanup --- src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs | 2 +- .../Hql/Ast/ANTLR/Tree/AggregateNode.cs | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs index 7d1ab9a0420..0f1c7a64603 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs @@ -313,7 +313,7 @@ private void EndFunctionTemplate(IASTNode m) private void OutAggregateFunctionName(IASTNode m) { var aggregateNode = (AggregateNode) m; - Out(aggregateNode.SqlFunctionName()); + Out(aggregateNode.FunctionName); } private void CommaBetweenParameters(String comma) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs index a88701a94da..a3887a56c01 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/AggregateNode.cs @@ -20,7 +20,18 @@ public AggregateNode(IToken token) { } - internal ISQLFunctionExtended SqlFunction => SessionFactoryHelper.FindSQLFunction(Text) as ISQLFunctionExtended; + public string FunctionName + { + get + { + if (SessionFactoryHelper.FindSQLFunction(Text) is ISQLFunctionExtended sqlFunction) + { + return sqlFunction.FunctionName; + } + + return Text; + } + } public override IType DataType { @@ -39,15 +50,5 @@ public override void SetScalarColumnText(int i) { ColumnHelper.GenerateSingleScalarColumn(ASTFactory, this, i); } - - public string SqlFunctionName() - { - if (SessionFactoryHelper.FindSQLFunction(Text) is ISQLFunctionExtended sqlFunction) - { - return sqlFunction.FunctionName; - } - - return Text; - } } }