From 38ad69f7ac1ac9bdb4d0f46f092940208a50ad8b Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Thu, 27 Feb 2020 00:08:36 +0200 Subject: [PATCH 01/11] Add DATALENGTH function to SqlServer --- .../SqlServerDbFunctionsExtensions.cs | 11 +++++ .../SqlServerDataLengthFunctionTranslator.cs | 41 +++++++++++++++++++ .../SqlServerMethodCallTranslatorProvider.cs | 1 + 3 files changed, 53 insertions(+) create mode 100644 src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index 794ee92f4c9..d92d500f2ad 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -966,5 +966,16 @@ public static TimeSpan TimeFromParts( int fractions, int precision) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(TimeFromParts))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + string arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs new file mode 100644 index 00000000000..7d01f836165 --- /dev/null +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal +{ + public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator + { + private readonly ISqlExpressionFactory _sqlExpressionFactory; + + private static readonly MethodInfo _methodInfo = typeof(SqlServerDbFunctionsExtensions) + .GetRuntimeMethod(nameof(SqlServerDbFunctionsExtensions.DataLength), new[] { typeof(DbFunctions), typeof(string) }); + + public SqlServerDataLengthFunctionTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) + { + _sqlExpressionFactory = sqlExpressionFactory; + } + + public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList arguments) + { + Check.NotNull(method, nameof(method)); + Check.NotNull(arguments, nameof(arguments)); + + return _methodInfo.Equals(method) + ? _sqlExpressionFactory.Function( + "DATALENGTH", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: new[] { true }, + _methodInfo.ReturnType) + : null; + } + } +} diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs index 502db911571..4404ddb1557 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs @@ -27,6 +27,7 @@ public SqlServerMethodCallTranslatorProvider([NotNull] RelationalMethodCallTrans new SqlServerNewGuidTranslator(sqlExpressionFactory), new SqlServerObjectToStringTranslator(sqlExpressionFactory), new SqlServerStringMethodTranslator(sqlExpressionFactory), + new SqlServerDataLengthFunctionTranslator(sqlExpressionFactory) }); } } From f59d00b8a3e1bb67066919b32e5beb3b17a9495e Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Thu, 27 Feb 2020 00:33:02 +0200 Subject: [PATCH 02/11] Add DATALENGTH function to SqlServer Fixes #19988 --- .../SqlServerDbFunctionsExtensions.cs | 88 +++++++++++++++++++ .../SqlServerDataLengthFunctionTranslator.cs | 75 ++++++++++++++-- 2 files changed, 156 insertions(+), 7 deletions(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index d92d500f2ad..df4aac8be22 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -977,5 +977,93 @@ public static TimeSpan TimeFromParts( [CanBeNull] this DbFunctions _, string arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + bool? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + double? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + decimal? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + DateTime? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + TimeSpan? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + DateTimeOffset? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + byte[] arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + + /// + /// Returns the number of bytes used to represent any expression. + /// + /// The DbFunctions instance. + /// The value to be examined for data length. + /// The number of bytes in the input value. + public static int? DataLength( + [CanBeNull] this DbFunctions _, + Guid? arg) + => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index 7d01f836165..90eb6e2af14 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -14,9 +15,66 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator { private readonly ISqlExpressionFactory _sqlExpressionFactory; + private const string DataLengthFunctionName = "DATALENGTH"; - private static readonly MethodInfo _methodInfo = typeof(SqlServerDbFunctionsExtensions) - .GetRuntimeMethod(nameof(SqlServerDbFunctionsExtensions.DataLength), new[] { typeof(DbFunctions), typeof(string) }); + private readonly Dictionary _methodInfoDataLengthMapping + = new Dictionary + { + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(string) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(bool?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(double?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(decimal?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(DateTime?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(TimeSpan?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(DateTimeOffset?) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(byte[]) }), + DataLengthFunctionName + }, + { + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(Guid?) }), + DataLengthFunctionName + } + }; public SqlServerDataLengthFunctionTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) { @@ -28,14 +86,17 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); - return _methodInfo.Equals(method) - ? _sqlExpressionFactory.Function( - "DATALENGTH", + if (_methodInfoDataLengthMapping.TryGetValue(method, out var sqlFunctionName)) + { + return _sqlExpressionFactory.Function( + sqlFunctionName, arguments.Skip(1), nullable: true, argumentsPropagateNullability: new[] { true }, - _methodInfo.ReturnType) - : null; + method.ReturnType); + } + + return null; } } } From 659e360f8d96d491cd1b0a5d432b2916f272f202 Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Fri, 28 Feb 2020 02:16:11 +0200 Subject: [PATCH 03/11] Code review fixes --- .../SqlServerDbFunctionsExtensions.cs | 18 ++-- .../SqlServerDataLengthFunctionTranslator.cs | 98 ++++++++----------- 2 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index df4aac8be22..b3a4dbb1dbe 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -973,7 +973,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, string arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -984,7 +984,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, bool? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -995,7 +995,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, double? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1006,7 +1006,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, decimal? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1017,7 +1017,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, DateTime? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1028,7 +1028,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, TimeSpan? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1039,7 +1039,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, DateTimeOffset? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1050,7 +1050,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, byte[] arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1061,7 +1061,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static int? DataLength( + public static long? DataLength( [CanBeNull] this DbFunctions _, Guid? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index 90eb6e2af14..ca395ffa2f0 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -15,65 +15,45 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator { private readonly ISqlExpressionFactory _sqlExpressionFactory; - private const string DataLengthFunctionName = "DATALENGTH"; - private readonly Dictionary _methodInfoDataLengthMapping - = new Dictionary + private readonly HashSet _methodInfoDataLengthMapping + = new HashSet { - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(string) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(bool?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(double?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(decimal?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(DateTime?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(TimeSpan?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(DateTimeOffset?) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(byte[]) }), - DataLengthFunctionName - }, - { - typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( - nameof(SqlServerDbFunctionsExtensions.DataLength), - new[] { typeof(DbFunctions), typeof(Guid?) }), - DataLengthFunctionName - } + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(string) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(bool?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(double?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(decimal?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(DateTime?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(TimeSpan?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(DateTimeOffset?) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(byte[]) }), + + typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( + nameof(SqlServerDbFunctionsExtensions.DataLength), + new[] { typeof(DbFunctions), typeof(Guid?) }) }; public SqlServerDataLengthFunctionTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) @@ -86,10 +66,10 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); - if (_methodInfoDataLengthMapping.TryGetValue(method, out var sqlFunctionName)) + if (_methodInfoDataLengthMapping.TryGetValue(method, out _)) { return _sqlExpressionFactory.Function( - sqlFunctionName, + "DATALENGTH", arguments.Skip(1), nullable: true, argumentsPropagateNullability: new[] { true }, From 1ed62b0a73c1334c33bfa515483a2e9849357473 Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Fri, 28 Feb 2020 03:46:15 +0200 Subject: [PATCH 04/11] Add tests --- .../SqlServerDataLengthFunctionTranslator.cs | 6 +-- .../NorthwindDbFunctionsQuerySqlServerTest.cs | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index ca395ffa2f0..723d7e16b34 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -14,9 +14,7 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator { - private readonly ISqlExpressionFactory _sqlExpressionFactory; - - private readonly HashSet _methodInfoDataLengthMapping + private static readonly HashSet _methodInfoDataLengthMapping = new HashSet { typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod( @@ -56,6 +54,8 @@ private readonly HashSet _methodInfoDataLengthMapping new[] { typeof(DbFunctions), typeof(Guid?) }) }; + private readonly ISqlExpressionFactory _sqlExpressionFactory; + public SqlServerDataLengthFunctionTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) { _sqlExpressionFactory = sqlExpressionFactory; diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs index 0a4fd174a8c..e4845fdfc46 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs @@ -967,6 +967,60 @@ FROM [Orders] AS [o] } } + [ConditionalFact] + public virtual void DataLength_column_compare() + { + using (var context = CreateContext()) + { + var count = context.Orders + .Count(c => c.OrderID < EF.Functions.DataLength(c.OrderDate)); + + Assert.Equal(0, count); + + AssertSql( + @"SELECT COUNT(*) +FROM [Orders] AS [o] +WHERE CAST([o].[OrderID] AS bigint) < DATALENGTH([o].[OrderDate])"); + } + } + + [ConditionalFact] + public virtual void DataLength_constant_compare() + { + using (var context = CreateContext()) + { + var count = context.Orders + .Count(c => 100 < EF.Functions.DataLength(c.OrderDate)); + + Assert.Equal(0, count); + + AssertSql( + @"SELECT COUNT(*) +FROM [Orders] AS [o] +WHERE CAST(100 AS bigint) < DATALENGTH([o].[OrderDate])"); + } + } + + [ConditionalFact] + public virtual void DataLength_compare_with_local_variable() + { + long lenght = 100; + using (var context = CreateContext()) + { + var count = context.Orders + .Count(c => lenght < EF.Functions.DataLength(c.OrderDate)); + + Assert.Equal(0, count); + + AssertSql( + @$"@__lenght_0='100' (Nullable = true) + +SELECT COUNT(*) +FROM [Orders] AS [o] +WHERE @__lenght_0 < DATALENGTH([o].[OrderDate])"); + } + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } From e2abd90f8e60771beef6f5e271e79adab5ac0d38 Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Fri, 28 Feb 2020 23:48:37 +0200 Subject: [PATCH 05/11] Fix test Public_api_arguments_should_have_not_null_annotation --- .../Extensions/SqlServerDbFunctionsExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index b3a4dbb1dbe..d98b9af4054 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -975,7 +975,7 @@ public static TimeSpan TimeFromParts( /// The number of bytes in the input value. public static long? DataLength( [CanBeNull] this DbFunctions _, - string arg) + [CanBeNull] string arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); /// From 3c4fcf020ae4d9878d8362821ccab3d0157d07cd Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Sat, 29 Feb 2020 00:04:12 +0200 Subject: [PATCH 06/11] Fix test Public_api_arguments_should_have_not_null_annotation --- .../Extensions/SqlServerDbFunctionsExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index d98b9af4054..f3307834649 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -1052,7 +1052,7 @@ public static TimeSpan TimeFromParts( /// The number of bytes in the input value. public static long? DataLength( [CanBeNull] this DbFunctions _, - byte[] arg) + [CanBeNull] byte[] arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); /// From cec3d513263dc2cacde8977a1dcbd96019098e2a Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Sun, 1 Mar 2020 13:56:51 +0200 Subject: [PATCH 07/11] Code review fixes --- .../Query/Internal/SqlServerDataLengthFunctionTranslator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index 723d7e16b34..4a8660f920e 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -66,7 +66,7 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); - if (_methodInfoDataLengthMapping.TryGetValue(method, out _)) + if (_methodInfoDataLengthMapping.Contains(method)) { return _sqlExpressionFactory.Function( "DATALENGTH", From 198bd9b1c3422d6827a7c678c0857bfc2f9f767a Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Wed, 4 Mar 2020 20:34:25 +0200 Subject: [PATCH 08/11] Sort TranslatorProviders --- .../Query/Internal/SqlServerMethodCallTranslatorProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs index 4404ddb1557..332d03b6125 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerMethodCallTranslatorProvider.cs @@ -18,6 +18,7 @@ public SqlServerMethodCallTranslatorProvider([NotNull] RelationalMethodCallTrans { new SqlServerByteArrayMethodTranslator(sqlExpressionFactory), new SqlServerConvertTranslator(sqlExpressionFactory), + new SqlServerDataLengthFunctionTranslator(sqlExpressionFactory), new SqlServerDateDiffFunctionsTranslator(sqlExpressionFactory), new SqlServerDateTimeMethodTranslator(sqlExpressionFactory), new SqlServerFromPartsFunctionTranslator(sqlExpressionFactory, typeMappingSource), @@ -26,8 +27,7 @@ public SqlServerMethodCallTranslatorProvider([NotNull] RelationalMethodCallTrans new SqlServerMathTranslator(sqlExpressionFactory), new SqlServerNewGuidTranslator(sqlExpressionFactory), new SqlServerObjectToStringTranslator(sqlExpressionFactory), - new SqlServerStringMethodTranslator(sqlExpressionFactory), - new SqlServerDataLengthFunctionTranslator(sqlExpressionFactory) + new SqlServerStringMethodTranslator(sqlExpressionFactory) }); } } From 48fca1e1388adee8b463cdb6a8bbfeafafcbf031 Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Sat, 7 Mar 2020 02:32:08 +0200 Subject: [PATCH 09/11] Changed long? to int? for DataLength function --- .../SqlServerDbFunctionsExtensions.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index f3307834649..6eef2786a8e 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -973,7 +973,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] string arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -984,7 +984,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, bool? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -995,7 +995,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, double? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1006,7 +1006,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, decimal? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1017,7 +1017,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, DateTime? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1028,7 +1028,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, TimeSpan? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1039,7 +1039,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, DateTimeOffset? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1050,7 +1050,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] byte[] arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); @@ -1061,7 +1061,7 @@ public static TimeSpan TimeFromParts( /// The DbFunctions instance. /// The value to be examined for data length. /// The number of bytes in the input value. - public static long? DataLength( + public static int? DataLength( [CanBeNull] this DbFunctions _, Guid? arg) => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); From cd4b3f2e4288d1d9ac546df563eed9542338aa96 Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Sun, 8 Mar 2020 23:22:33 +0200 Subject: [PATCH 10/11] Fix tests --- .../Query/NorthwindDbFunctionsQuerySqlServerTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs index e4845fdfc46..002bdb5c910 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs @@ -980,7 +980,7 @@ public virtual void DataLength_column_compare() AssertSql( @"SELECT COUNT(*) FROM [Orders] AS [o] -WHERE CAST([o].[OrderID] AS bigint) < DATALENGTH([o].[OrderDate])"); +WHERE [o].[OrderID] < DATALENGTH([o].[OrderDate])"); } } @@ -997,14 +997,14 @@ public virtual void DataLength_constant_compare() AssertSql( @"SELECT COUNT(*) FROM [Orders] AS [o] -WHERE CAST(100 AS bigint) < DATALENGTH([o].[OrderDate])"); +WHERE 100 < DATALENGTH([o].[OrderDate])"); } } [ConditionalFact] public virtual void DataLength_compare_with_local_variable() { - long lenght = 100; + int? lenght = 100; using (var context = CreateContext()) { var count = context.Orders From 6a2e2e3670770b97974fecba7aa28fd702ea8b0f Mon Sep 17 00:00:00 2001 From: Roma Marusyk Date: Tue, 24 Mar 2020 14:13:29 +0200 Subject: [PATCH 11/11] Add test --- .../SqlServerDbFunctionsExtensions.cs | 18 ++++++++--------- .../SqlServerDataLengthFunctionTranslator.cs | 20 +++++++++++++++++++ .../Query/GearsOfWarQuerySqlServerTest.cs | 17 ++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index 6eef2786a8e..30ab9fb03ee 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -976,7 +976,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] string arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -987,7 +987,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, bool? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -998,7 +998,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, double? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1009,7 +1009,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, decimal? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1020,7 +1020,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, DateTime? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1031,7 +1031,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, TimeSpan? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1042,7 +1042,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, DateTimeOffset? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1053,7 +1053,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] byte[] arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1064,6 +1064,6 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, Guid? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index 4a8660f920e..7915666a7e5 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -14,6 +14,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator { + private static readonly List _longReturningTypes = new List { "nvarchar(max)", "varchar(max)", "varbinary(max)" }; + private static readonly HashSet _methodInfoDataLengthMapping = new HashSet { @@ -68,6 +70,24 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method if (_methodInfoDataLengthMapping.Contains(method)) { + var argument = arguments[1]; + if (argument.TypeMapping == null) + { + argument = _sqlExpressionFactory.ApplyDefaultTypeMapping(argument); + } + + if (_longReturningTypes.Contains(argument.TypeMapping.StoreType)) + { + var result = _sqlExpressionFactory.Function( + "DATALENGTH", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: new[] { true }, + typeof(long)); + + return _sqlExpressionFactory.Convert(result, method.ReturnType); + } + return _sqlExpressionFactory.Function( "DATALENGTH", arguments.Skip(1), diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d30fafa4a1a..a7cb439b0d3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -2,7 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -6976,6 +6979,20 @@ FROM [Gears] AS [g] ORDER BY [g].[Nickname]"); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public async Task DataLength_function_for_string_parameter(bool async) + { + await AssertQueryScalar( + async, + ss => ss.Set().Select(m => EF.Functions.DataLength(m.CodeName)), + ss => ss.Set().Select(m => (int?)(m.CodeName.Length * 2))); + + AssertSql( + @"SELECT CAST(DATALENGTH([m].[CodeName]) AS int) +FROM [Missions] AS [m]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }