From 168145cef6bb2b0b3b0866446b9371b86b9c3fbe Mon Sep 17 00:00:00 2001 From: Andrew Peters Date: Fri, 2 Feb 2018 10:52:23 -0800 Subject: [PATCH] Fix #9558 - Relational: UDF calls are generated unquoted Tweak SQL gen to delimit function calls that have a schema. --- src/EFCore.Relational/DbFunctionAttribute.cs | 7 +- .../Expressions/SqlFunctionExpression.cs | 2 +- .../Query/Sql/DefaultQuerySqlGenerator.cs | 14 +++- .../Query/QueryBugsTest.cs | 4 +- .../Query/UdfDbFunctionSqlServerTests.cs | 64 +++++++++---------- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/EFCore.Relational/DbFunctionAttribute.cs b/src/EFCore.Relational/DbFunctionAttribute.cs index 5e2ad978081..65896f54faf 100644 --- a/src/EFCore.Relational/DbFunctionAttribute.cs +++ b/src/EFCore.Relational/DbFunctionAttribute.cs @@ -42,7 +42,7 @@ public DbFunctionAttribute([NotNull] string functionName, [CanBeNull] string sch /// public virtual string FunctionName { - get { return _functionName; } + get => _functionName; [param: NotNull] set { @@ -57,8 +57,9 @@ public virtual string FunctionName /// public virtual string Schema { - get { return _schema; } - [param: CanBeNull] set { _schema = value; } + get => _schema; + [param: CanBeNull] + set => _schema = value; } } } diff --git a/src/EFCore.Relational/Query/Expressions/SqlFunctionExpression.cs b/src/EFCore.Relational/Query/Expressions/SqlFunctionExpression.cs index aeb43293565..db50dabb556 100644 --- a/src/EFCore.Relational/Query/Expressions/SqlFunctionExpression.cs +++ b/src/EFCore.Relational/Query/Expressions/SqlFunctionExpression.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Expressions /// /// Represents a SQL function call expression. /// - [DebuggerDisplay("{ToString()}")] + [DebuggerDisplay("{" + nameof(ToString) + "()}")] public class SqlFunctionExpression : Expression { private readonly ReadOnlyCollection _arguments; diff --git a/src/EFCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs b/src/EFCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs index 540da06dc53..fc003415a15 100644 --- a/src/EFCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs @@ -1409,11 +1409,15 @@ public virtual Expression VisitLike(LikeExpression likeExpression) public virtual Expression VisitSqlFunction(SqlFunctionExpression sqlFunctionExpression) { var parentTypeMapping = _typeMapping; + _typeMapping = null; + var wroteSchema = false; + if (sqlFunctionExpression.Instance != null) { Visit(sqlFunctionExpression.Instance); + _relationalCommandBuilder.Append("."); } else if (!string.IsNullOrWhiteSpace(sqlFunctionExpression.Schema)) @@ -1421,12 +1425,20 @@ public virtual Expression VisitSqlFunction(SqlFunctionExpression sqlFunctionExpr _relationalCommandBuilder .Append(SqlGenerator.DelimitIdentifier(sqlFunctionExpression.Schema)) .Append("."); + + wroteSchema = true; } - _relationalCommandBuilder.Append(sqlFunctionExpression.FunctionName); + _relationalCommandBuilder + .Append( + wroteSchema + ? SqlGenerator.DelimitIdentifier(sqlFunctionExpression.FunctionName) + : sqlFunctionExpression.FunctionName); + _relationalCommandBuilder.Append("("); _typeMapping = null; + GenerateList(sqlFunctionExpression.Arguments); _relationalCommandBuilder.Append(")"); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index d9d3ee5bf00..3ab9cfedf62 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -2245,7 +2245,7 @@ public void Default_schema_applied_when_no_function_schema() Assert.Equal(2, result); AssertSql( - @"SELECT TOP(2) [foo].AddOne([w].[Val]) + @"SELECT TOP(2) [foo].[AddOne]([w].[Val]) FROM [foo].[Widgets] AS [w] WHERE [w].[Val] = 1"); } @@ -2264,7 +2264,7 @@ public void Default_schema_function_schema_overrides() Assert.Equal(3, result); AssertSql( - @"SELECT TOP(2) [dbo].AddTwo([w].[Val]) + @"SELECT TOP(2) [dbo].[AddTwo]([w].[Val]) FROM [foo].[Widgets] AS [w] WHERE [w].[Val] = 1"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs index 90ded012b4a..06423b17486 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs @@ -313,7 +313,7 @@ public void Scalar_Function_Constant_Parameter_Static() AssertSql( @"@__customerId_0='1' -SELECT [dbo].CustomerOrderCount(@__customerId_0) +SELECT [dbo].[CustomerOrderCount](@__customerId_0) FROM [Customers] AS [c]"); } } @@ -335,7 +335,7 @@ public void Scalar_Function_Anonymous_Type_Select_Correlated_Static() Assert.Equal(3, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount([c].[Id]) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount]([c].[Id]) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 1"); } @@ -358,7 +358,7 @@ public void Scalar_Function_Anonymous_Type_Select_Not_Correlated_Static() Assert.Equal(3, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(1) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](1) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 1"); } @@ -386,7 +386,7 @@ public void Scalar_Function_Anonymous_Type_Select_Parameter_Static() @"@__customerId_1='1' @__customerId_0='1' -SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(@__customerId_1) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](@__customerId_1) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_0"); } @@ -416,7 +416,7 @@ public void Scalar_Function_Anonymous_Type_Select_Nested_Static() @__customerId_2='3' @__customerId_0='3' -SELECT TOP(2) [c].[LastName], [dbo].StarValue(@__starCount_1, [dbo].CustomerOrderCount(@__customerId_2)) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[StarValue](@__starCount_1, [dbo].[CustomerOrderCount](@__customerId_2)) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_0"); } @@ -436,7 +436,7 @@ where UDFSqlContext.IsTopCustomerStatic(c.Id) AssertSql( @"SELECT LOWER(CONVERT(VARCHAR(11), [c].[Id])) FROM [Customers] AS [c] -WHERE [dbo].IsTopCustomer([c].[Id]) = 1"); +WHERE [dbo].[IsTopCustomer]([c].[Id]) = 1"); } } @@ -458,7 +458,7 @@ where UDFSqlContext.GetCustomerWithMostOrdersAfterDateStatic(startDate) == c.Id SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [dbo].GetCustomerWithMostOrdersAfterDate(@__startDate_0) = [c].[Id]"); +WHERE [dbo].[GetCustomerWithMostOrdersAfterDate](@__startDate_0) = [c].[Id]"); } } @@ -481,7 +481,7 @@ public void Scalar_Function_Where_Parameter_Static() SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [c].[Id] = [dbo].GetCustomerWithMostOrdersAfterDate([dbo].GetReportingPeriodStartDate(@__period_0))"); +WHERE [c].[Id] = [dbo].[GetCustomerWithMostOrdersAfterDate]([dbo].[GetReportingPeriodStartDate](@__period_0))"); } } @@ -501,7 +501,7 @@ public void Scalar_Function_Where_Nested_Static() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [c].[Id] = [dbo].GetCustomerWithMostOrdersAfterDate([dbo].GetReportingPeriodStartDate(0))"); +WHERE [c].[Id] = [dbo].[GetCustomerWithMostOrdersAfterDate]([dbo].[GetReportingPeriodStartDate](0))"); } } @@ -523,7 +523,7 @@ public void Scalar_Function_Let_Correlated_Static() Assert.Equal(2, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount([c].[Id]) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount]([c].[Id]) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 2"); } @@ -547,7 +547,7 @@ public void Scalar_Function_Let_Not_Correlated_Static() Assert.Equal(2, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(2) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](2) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 2"); } @@ -576,7 +576,7 @@ public void Scalar_Function_Let_Not_Parameter_Static() @"@__customerId_0='2' @__customerId_1='2' -SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(@__customerId_0) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](@__customerId_0) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_1"); } @@ -607,7 +607,7 @@ public void Scalar_Function_Let_Nested_Static() @__customerId_1='1' @__customerId_2='1' -SELECT TOP(2) [c].[LastName], [dbo].StarValue(@__starCount_0, [dbo].CustomerOrderCount(@__customerId_1)) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[StarValue](@__starCount_0, [dbo].[CustomerOrderCount](@__customerId_1)) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_2"); } @@ -823,7 +823,7 @@ public void Scalar_Nested_Function_BCL_UDF_Static() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE 3 = ABS([dbo].CustomerOrderCount([c].[Id]))"); +WHERE 3 = ABS([dbo].[CustomerOrderCount]([c].[Id]))"); } } @@ -857,7 +857,7 @@ public void Scalar_Nested_Function_UDF_BCL_Static() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE 3 = [dbo].CustomerOrderCount(ABS([c].[Id]))"); +WHERE 3 = [dbo].[CustomerOrderCount](ABS([c].[Id]))"); } } @@ -873,7 +873,7 @@ public void Nullable_navigation_property_access_preserves_schema_for_sql_functio Assert.Equal("Customer", result); AssertSql( - @"SELECT TOP(1) [dbo].IdentityString([o.Customer].[FirstName]) + @"SELECT TOP(1) [dbo].[IdentityString]([o.Customer].[FirstName]) FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerId] = [o.Customer].[Id] ORDER BY [o].[Id]"); @@ -900,7 +900,7 @@ public void Scalar_Function_Non_Static() Assert.Equal(custName.LastName, "$$One"); AssertSql( - @"SELECT TOP(2) [dbo].StarValue(4, [c].[Id]) AS [Id], [dbo].DollarValue(2, [c].[LastName]) AS [LastName] + @"SELECT TOP(2) [dbo].[StarValue](4, [c].[Id]) AS [Id], [dbo].[DollarValue](2, [c].[LastName]) AS [LastName] FROM [Customers] AS [c] WHERE [c].[Id] = 1"); } @@ -977,7 +977,7 @@ public void Scalar_Function_Constant_Parameter_Instance() AssertSql( @"@__customerId_1='1' -SELECT [dbo].CustomerOrderCount(@__customerId_1) +SELECT [dbo].[CustomerOrderCount](@__customerId_1) FROM [Customers] AS [c]"); } } @@ -999,7 +999,7 @@ public void Scalar_Function_Anonymous_Type_Select_Correlated_Instance() Assert.Equal(3, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount([c].[Id]) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount]([c].[Id]) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 1"); } @@ -1022,7 +1022,7 @@ public void Scalar_Function_Anonymous_Type_Select_Not_Correlated_Instance() Assert.Equal(3, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(1) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](1) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 1"); } @@ -1050,7 +1050,7 @@ public void Scalar_Function_Anonymous_Type_Select_Parameter_Instance() @"@__customerId_2='1' @__customerId_0='1' -SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(@__customerId_2) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](@__customerId_2) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_0"); } @@ -1080,7 +1080,7 @@ public void Scalar_Function_Anonymous_Type_Select_Nested_Instance() @__customerId_4='3' @__customerId_0='3' -SELECT TOP(2) [c].[LastName], [dbo].StarValue(@__starCount_2, [dbo].CustomerOrderCount(@__customerId_4)) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[StarValue](@__starCount_2, [dbo].[CustomerOrderCount](@__customerId_4)) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_0"); } @@ -1100,7 +1100,7 @@ where context.IsTopCustomerInstance(c.Id) AssertSql( @"SELECT LOWER(CONVERT(VARCHAR(11), [c].[Id])) FROM [Customers] AS [c] -WHERE [dbo].IsTopCustomer([c].[Id]) = 1"); +WHERE [dbo].[IsTopCustomer]([c].[Id]) = 1"); } } @@ -1122,7 +1122,7 @@ where context.GetCustomerWithMostOrdersAfterDateInstance(startDate) == c.Id SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [dbo].GetCustomerWithMostOrdersAfterDate(@__startDate_1) = [c].[Id]"); +WHERE [dbo].[GetCustomerWithMostOrdersAfterDate](@__startDate_1) = [c].[Id]"); } } @@ -1145,7 +1145,7 @@ public void Scalar_Function_Where_Parameter_Instance() SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [c].[Id] = [dbo].GetCustomerWithMostOrdersAfterDate([dbo].GetReportingPeriodStartDate(@__period_2))"); +WHERE [c].[Id] = [dbo].[GetCustomerWithMostOrdersAfterDate]([dbo].[GetReportingPeriodStartDate](@__period_2))"); } } @@ -1165,7 +1165,7 @@ public void Scalar_Function_Where_Nested_Instance() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE [c].[Id] = [dbo].GetCustomerWithMostOrdersAfterDate([dbo].GetReportingPeriodStartDate(0))"); +WHERE [c].[Id] = [dbo].[GetCustomerWithMostOrdersAfterDate]([dbo].[GetReportingPeriodStartDate](0))"); } } @@ -1187,7 +1187,7 @@ public void Scalar_Function_Let_Correlated_Instance() Assert.Equal(2, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount([c].[Id]) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount]([c].[Id]) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 2"); } @@ -1211,7 +1211,7 @@ public void Scalar_Function_Let_Not_Correlated_Instance() Assert.Equal(2, cust.OrderCount); AssertSql( - @"SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(2) AS [OrderCount] + @"SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](2) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = 2"); } @@ -1240,7 +1240,7 @@ public void Scalar_Function_Let_Not_Parameter_Instance() @"@__8__locals1_customerId_1='2' @__8__locals1_customerId_2='2' -SELECT TOP(2) [c].[LastName], [dbo].CustomerOrderCount(@__8__locals1_customerId_1) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[CustomerOrderCount](@__8__locals1_customerId_1) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__8__locals1_customerId_2"); } @@ -1271,7 +1271,7 @@ public void Scalar_Function_Let_Nested_Instance() @__customerId_3='1' @__customerId_4='1' -SELECT TOP(2) [c].[LastName], [dbo].StarValue(@__starCount_1, [dbo].CustomerOrderCount(@__customerId_3)) AS [OrderCount] +SELECT TOP(2) [c].[LastName], [dbo].[StarValue](@__starCount_1, [dbo].[CustomerOrderCount](@__customerId_3)) AS [OrderCount] FROM [Customers] AS [c] WHERE [c].[Id] = @__customerId_4"); } @@ -1487,7 +1487,7 @@ public void Scalar_Nested_Function_BCL_UDF_Instance() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE 3 = ABS([dbo].CustomerOrderCount([c].[Id]))"); +WHERE 3 = ABS([dbo].[CustomerOrderCount]([c].[Id]))"); } } @@ -1521,7 +1521,7 @@ public void Scalar_Nested_Function_UDF_BCL_Instance() AssertSql( @"SELECT TOP(2) [c].[Id] FROM [Customers] AS [c] -WHERE 3 = [dbo].CustomerOrderCount(ABS([c].[Id]))"); +WHERE 3 = [dbo].[CustomerOrderCount](ABS([c].[Id]))"); } }