Skip to content

Commit

Permalink
Merge branch 'datalength'
Browse files Browse the repository at this point in the history
  • Loading branch information
maumar committed Mar 25, 2020
2 parents 5031ffd + 6a2e2e3 commit 7d110c0
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 1 deletion.
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);
}

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]
}
}

[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 [o].[OrderID] < 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 100 < DATALENGTH([o].[OrderDate])");
}
}

[ConditionalFact]
public virtual void DataLength_compare_with_local_variable()
{
int? 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);
}
Expand Down

0 comments on commit 7d110c0

Please sign in to comment.