Skip to content

Commit

Permalink
SqlServer: Translate byte array access/First
Browse files Browse the repository at this point in the history
Part of #16428
  • Loading branch information
smitpatel committed Dec 1, 2020
1 parent 758c0b9 commit 6c19b23
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ public SqlServerByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExp
_sqlExpressionFactory.Constant(0));
}

if (method.IsGenericMethod
&& method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)
&& arguments[0].Type == typeof(byte[]))
{
return _sqlExpressionFactory.Convert(
_sqlExpressionFactory.Function(
"SUBSTRING",
new SqlExpression[]
{
arguments[0],
_sqlExpressionFactory.Constant(1),
_sqlExpressionFactory.Constant(1)
},
nullable: true,
argumentsPropagateNullability: new[] { true, true, true },
typeof(byte[])),
method.ReturnType);
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,33 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));

if (binaryExpression.NodeType == ExpressionType.ArrayIndex
&& binaryExpression.Left.Type == typeof(byte[]))
{
var left = Visit(binaryExpression.Left);
var right = Visit(binaryExpression.Right);

if (left is SqlExpression leftSql
&& right is SqlExpression rightSql)
{
return Dependencies.SqlExpressionFactory.Convert(
Dependencies.SqlExpressionFactory.Function(
"SUBSTRING",
new SqlExpression[]
{
leftSql,
Dependencies.SqlExpressionFactory.Add(
Dependencies.SqlExpressionFactory.ApplyDefaultTypeMapping(rightSql),
Dependencies.SqlExpressionFactory.Constant(1)),
Dependencies.SqlExpressionFactory.Constant(1)
},
nullable: true,
argumentsPropagateNullability: new[] { true, true, true },
typeof(byte[])),
binaryExpression.Type);
}
}

return !(base.VisitBinary(binaryExpression) is SqlExpression visitedExpression)
? QueryCompilationContext.NotTranslatedExpression
: visitedExpression is SqlBinaryExpression sqlBinary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

#nullable enable
Expand All @@ -23,16 +24,20 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal
public class SqliteByteArrayMethodTranslator : IMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SqliteByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
public SqliteByteArrayMethodTranslator(
[NotNull] ISqlExpressionFactory sqlExpressionFactory,
[NotNull] IRelationalTypeMappingSource typeMappingSource)
{
_sqlExpressionFactory = sqlExpressionFactory;
_typeMappingSource = typeMappingSource;
}

/// <summary>
Expand Down Expand Up @@ -76,6 +81,32 @@ public SqliteByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExpres
_sqlExpressionFactory.Constant(0));
}

// See issue#16428
//if (method.IsGenericMethod
// && method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)
// && arguments[0].Type == typeof(byte[]))
//{
// return _sqlExpressionFactory.Function(
// "unicode",
// new SqlExpression[]
// {
// _sqlExpressionFactory.Function(
// "substr",
// new SqlExpression[]
// {
// arguments[0],
// _sqlExpressionFactory.Constant(1),
// _sqlExpressionFactory.Constant(1)
// },
// nullable: true,
// argumentsPropagateNullability: new[] { true, true, true },
// typeof(byte[]))
// },
// nullable: true,
// argumentsPropagateNullability: new[] { true },
// method.ReturnType);
//}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public SqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslat
AddTranslators(
new IMethodCallTranslator[]
{
new SqliteByteArrayMethodTranslator(sqlExpressionFactory),
new SqliteByteArrayMethodTranslator(sqlExpressionFactory, dependencies.RelationalTypeMappingSource),
new SqliteCharMethodTranslator(sqlExpressionFactory),
new SqliteDateTimeAddTranslator(sqlExpressionFactory),
new SqliteGlobMethodTranslator(sqlExpressionFactory),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,40 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));

// See issue#16428
//if (binaryExpression.NodeType == ExpressionType.ArrayIndex
// && binaryExpression.Left.Type == typeof(byte[]))
//{
// var left = Visit(binaryExpression.Left);
// var right = Visit(binaryExpression.Right);

// if (left is SqlExpression leftSql
// && right is SqlExpression rightSql)
// {
// return Dependencies.SqlExpressionFactory.Function(
// "unicode",
// new SqlExpression[]
// {
// Dependencies.SqlExpressionFactory.Function(
// "substr",
// new SqlExpression[]
// {
// leftSql,
// Dependencies.SqlExpressionFactory.Add(
// Dependencies.SqlExpressionFactory.ApplyDefaultTypeMapping(rightSql),
// Dependencies.SqlExpressionFactory.Constant(1)),
// Dependencies.SqlExpressionFactory.Constant(1)
// },
// nullable: true,
// argumentsPropagateNullability: new[] { true, true, true },
// typeof(byte[]))
// },
// nullable: true,
// argumentsPropagateNullability: new[] { true },
// binaryExpression.Type);
// }
//}

if (!(base.VisitBinary(binaryExpression) is SqlExpression visitedExpression))
{
return QueryCompilationContext.NotTranslatedExpression;
Expand Down
76 changes: 52 additions & 24 deletions test/EFCore.Specification.Tests/MonsterFixupTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,17 @@ public virtual void Composite_fixup_happens_when_FKs_change_test()
var productReview2 = context.ProductReviews.Single(e => e.Review.StartsWith("Good"));
var productReview3 = context.ProductReviews.Single(e => e.Review.StartsWith("Eeky"));

// Issue #16428
var productPhotos = context.ProductPhotos.ToList();
var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
// See issue#16428
var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
var productPhoto1 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
: context.ProductPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
: context.ProductPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
: context.ProductPhotos.Single(e => e.Photo[0] == 105);

var productWebFeature1 = context.ProductWebFeatures.Single(e => e.Heading.StartsWith("Waffle"));
var productWebFeature2 = context.ProductWebFeatures.Single(e => e.Heading.StartsWith("What"));
Expand Down Expand Up @@ -834,10 +840,19 @@ protected void SimpleVerification()
new[] { "Better than Tarqies!", "Eeky says yes!", "Good with maple syrup." },
context.ProductReviews.Select(c => c.Review).OrderBy(n => n));

// Issue #16428
Assert.Equal(
new[] { "101", "103", "105" },
context.ProductPhotos.ToList().Select(c => c.Photo.First().ToString()).OrderBy(n => n));
// See issue#16428
if (context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
{
Assert.Equal(
new[] { "101", "103", "105" },
context.ProductPhotos.ToList().Select(c => c.Photo.First().ToString()).OrderBy(n => n));
}
else
{
Assert.Equal(
new[] { "101", "103", "105" },
context.ProductPhotos.Select(c => c.Photo.First().ToString()).OrderBy(n => n));
}

Assert.Equal(
new[] { "Waffle Style", "What does the waffle say?" },
Expand All @@ -847,7 +862,6 @@ protected void SimpleVerification()
new[] { "Ants By Boris", "Trading As Trent" },
context.Suppliers.Select(c => c.Name).OrderBy(n => n));

// Issue #16428
Assert.Equal(
new[] { "201", "202" },
context.SupplierLogos.ToList().SelectMany(c => c.Logo).Select(l => l.ToString()).OrderBy(n => n));
Expand Down Expand Up @@ -1027,11 +1041,17 @@ protected void FkVerification()
Assert.Equal(product1.ProductId, productReview2.ProductId);
Assert.Equal(product2.ProductId, productReview3.ProductId);

// Issue #16428
var productPhotos = context.ProductPhotos.ToList();
var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
// See issue#16428
var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
var productPhoto1 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
: context.ProductPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
: context.ProductPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
: context.ProductPhotos.Single(e => e.Photo[0] == 105);

Assert.Equal(product1.ProductId, productPhoto1.ProductId);
Assert.Equal(product1.ProductId, productPhoto2.ProductId);
Expand All @@ -1050,8 +1070,9 @@ protected void FkVerification()
var supplier1 = context.Suppliers.Single(e => e.Name.StartsWith("Trading"));
var supplier2 = context.Suppliers.Single(e => e.Name.StartsWith("Ants"));

// Issue #16428
var supplierLogo1 = context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201);
var supplierLogo1 = sqlite
? context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201)
: context.SupplierLogos.Single(e => e.Logo[0] == 201);

Assert.Equal(supplier1.SupplierId, supplierLogo1.SupplierId);

Expand Down Expand Up @@ -1293,11 +1314,17 @@ protected void NavigationVerification()

Assert.True(product3.Reviews == null || product3.Reviews.Count == 0);

// Issue #16428
var productPhotos = context.ProductPhotos.ToList();
var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
// See issue#16428
var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
var productPhoto1 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
: context.ProductPhotos.Single(e => e.Photo[0] == 101);
var productPhoto2 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
: context.ProductPhotos.Single(e => e.Photo[0] == 103);
var productPhoto3 = sqlite
? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
: context.ProductPhotos.Single(e => e.Photo[0] == 105);

Assert.Equal(
new[] { productPhoto1, productPhoto2 },
Expand Down Expand Up @@ -1327,8 +1354,9 @@ protected void NavigationVerification()
var supplier1 = context.Suppliers.Single(e => e.Name.StartsWith("Trading"));
var supplier2 = context.Suppliers.Single(e => e.Name.StartsWith("Ants"));

// Issue #16428
var supplierLogo1 = context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201);
var supplierLogo1 = sqlite
? context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201)
: context.SupplierLogos.Single(e => e.Logo[0] == 201);

Assert.Same(supplierLogo1, supplier1.Logo);

Expand Down

0 comments on commit 6c19b23

Please sign in to comment.