Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DATALENGTH function to SqlServer #20079

Merged
merged 11 commits into from
Mar 25, 2020
99 changes: 99 additions & 0 deletions src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -966,5 +966,104 @@ public static TimeSpan TimeFromParts(
int fractions,
int precision)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(TimeFromParts)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
[CanBeNull] string arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
bool? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
double? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
decimal? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
DateTime? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
TimeSpan? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
DateTimeOffset? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
[CanBeNull] byte[] arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));

/// <summary>
/// Returns the number of bytes used to represent any expression.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="arg">The value to be examined for data length.</param>
/// <returns>The number of bytes in the input value.</returns>
public static int? DataLength(
[CanBeNull] this DbFunctions _,
Guid? arg)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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;
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 static readonly List<string> _longReturningTypes = new List<string> { "nvarchar(max)", "varchar(max)", "varbinary(max)" };

private static readonly HashSet<MethodInfo> _methodInfoDataLengthMapping
= new HashSet<MethodInfo>
{
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?) })
};

private readonly ISqlExpressionFactory _sqlExpressionFactory;

public SqlServerDataLengthFunctionTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}

public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments)
{
Check.NotNull(method, nameof(method));
Check.NotNull(arguments, nameof(arguments));

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),
nullable: true,
argumentsPropagateNullability: new[] { true },
method.ReturnType);
}
Marusyk marked this conversation as resolved.
Show resolved Hide resolved

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -26,7 +27,7 @@ public SqlServerMethodCallTranslatorProvider([NotNull] RelationalMethodCallTrans
new SqlServerMathTranslator(sqlExpressionFactory),
new SqlServerNewGuidTranslator(sqlExpressionFactory),
new SqlServerObjectToStringTranslator(sqlExpressionFactory),
new SqlServerStringMethodTranslator(sqlExpressionFactory),
new SqlServerStringMethodTranslator(sqlExpressionFactory)
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Mission>().Select(m => EF.Functions.DataLength(m.CodeName)),
ss => ss.Set<Mission>().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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,60 @@ FROM [Orders] AS [o]
}