From 566188451a4e435b5a271e6381ec2193daa8f9de Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 21 Jul 2021 22:20:46 +0300 Subject: [PATCH] Keep guessed parameter type in hql --- src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs | 10 ++++++++++ src/NHibernate.Test/Linq/LinqQuerySamples.cs | 10 ++++++++++ src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs | 5 ++++- src/NHibernate/Hql/Ast/ANTLR/Tree/ParameterNode.cs | 2 ++ src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs | 9 +++++++-- src/NHibernate/Linq/Visitors/ParameterTypeLocator.cs | 9 ++++++--- src/NHibernate/Param/NamedParameter.cs | 1 + 7 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs b/src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs index 6e68c56ca1b..715a51d9a5b 100644 --- a/src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs +++ b/src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs @@ -1385,5 +1385,15 @@ public void ReplaceFunctionWithNullArgumentAsync() }, Throws.Nothing, "Expected REPLACE(FirstName, LastName, NULL) to be supported"); Assert.That(results, Is.Not.Null); } + + [Test(Description = "GH-2860")] + public async Task StringFormatWithTrimAsync() + { + var q = + from e in db.Employees + select new {Name = $"{e.FirstName} {e.LastName}".Trim(), Phone = e.Address.PhoneNumber}; + var items = await (q.ToListAsync()); + Assert.AreEqual(9, items.Count); + } } } diff --git a/src/NHibernate.Test/Linq/LinqQuerySamples.cs b/src/NHibernate.Test/Linq/LinqQuerySamples.cs index 22035aae7cc..2e3d8066c17 100755 --- a/src/NHibernate.Test/Linq/LinqQuerySamples.cs +++ b/src/NHibernate.Test/Linq/LinqQuerySamples.cs @@ -1970,6 +1970,16 @@ public void ReplaceFunctionWithNullArgument() }, Throws.Nothing, "Expected REPLACE(FirstName, LastName, NULL) to be supported"); Assert.That(results, Is.Not.Null); } + + [Test(Description = "GH-2860")] + public void StringFormatWithTrim() + { + var q = + from e in db.Employees + select new {Name = $"{e.FirstName} {e.LastName}".Trim(), Phone = e.Address.PhoneNumber}; + var items = q.ToList(); + Assert.AreEqual(9, items.Count); + } } public class ParentChildBatch diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index dd3fa852c75..578d9331153 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -1070,7 +1070,10 @@ IASTNode GenerateNamedParameter(IASTNode delimiterNode, IASTNode nameNode) { // Add the parameter type information so that we are able to calculate functions return types // when the parameter is used as an argument. - parameter.ExpectedType = namedParameter.Type; + if (namedParameter.IsGuessedType) + parameter.GuessedType = namedParameter.Type; + else + parameter.ExpectedType = namedParameter.Type; } _parameters.Add(paramSpec); diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/ParameterNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/ParameterNode.cs index 8d0a83a3f04..22de5913722 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/ParameterNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/ParameterNode.cs @@ -49,6 +49,8 @@ public IType ExpectedType } } + internal IType GuessedType { get; set; } + public override SqlString RenderText(ISessionFactoryImplementor sessionFactory) { int count; diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs index a3e4c9af4e1..557cc185548 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs @@ -153,9 +153,14 @@ public void InitializeExplicitSelectClause(FromClause fromClause) else { IType type = expr.DataType; - if (type == null && !(expr is ParameterNode)) + if (type == null) { - throw new QueryException("No data type for node: " + expr.GetType().Name + " " + new ASTPrinter().ShowAsString((IASTNode)expr, "")); + if (expr is ParameterNode param) + { + type = param.GuessedType; + } + else + throw new QueryException("No data type for node: " + expr.GetType().Name + " " + new ASTPrinter().ShowAsString((IASTNode)expr, "")); } //sqlResultTypeList.add( type ); diff --git a/src/NHibernate/Linq/Visitors/ParameterTypeLocator.cs b/src/NHibernate/Linq/Visitors/ParameterTypeLocator.cs index 0be307fce8b..de5f3511e23 100644 --- a/src/NHibernate/Linq/Visitors/ParameterTypeLocator.cs +++ b/src/NHibernate/Linq/Visitors/ParameterTypeLocator.cs @@ -93,7 +93,8 @@ internal static void SetParameterTypes( continue; } - namedParameter.Type = GetParameterType(sessionFactory, constantExpressions, visitor, namedParameter); + namedParameter.Type = GetParameterType(sessionFactory, constantExpressions, visitor, namedParameter, out var tryProcessInHql); + namedParameter.IsGuessedType = tryProcessInHql; } } @@ -145,8 +146,10 @@ private static IType GetParameterType( ISessionFactoryImplementor sessionFactory, HashSet constantExpressions, ConstantTypeLocatorVisitor visitor, - NamedParameter namedParameter) + NamedParameter namedParameter, + out bool tryProcessInHql) { + tryProcessInHql = false; // All constant expressions have the same type/value var constantExpression = constantExpressions.First(); var constantType = constantExpression.Type.UnwrapIfNullable(); @@ -158,7 +161,7 @@ private static IType GetParameterType( if (visitor.NotGuessableConstants.Contains(constantExpression) && constantExpression.Value != null) { - return null; + tryProcessInHql = true; } // No related MemberExpressions was found, guess the type by value or its type when null. diff --git a/src/NHibernate/Param/NamedParameter.cs b/src/NHibernate/Param/NamedParameter.cs index a9a9b67de2b..93af89daa0d 100644 --- a/src/NHibernate/Param/NamedParameter.cs +++ b/src/NHibernate/Param/NamedParameter.cs @@ -20,6 +20,7 @@ internal NamedParameter(string name, object value, IType type, bool isCollection public string Name { get; private set; } public object Value { get; internal set; } public IType Type { get; internal set; } + internal bool IsGuessedType { get; set; } public virtual bool IsCollection { get; }