SqlExpressionFactory can't be used inside SqlGenerator so create the expressions manually. Fixes queries with conversions

pull/131/head
Christopher Jolly 3 years ago
parent ca0feb49d3
commit b533695713

@ -2,17 +2,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using EntityFrameworkCore.Jet.Data;
using System.Linq;
using System.Linq.Expressions;
using EntityFrameworkCore.Jet.Infrastructure.Internal;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage;
using EntityFrameworkCore.Jet.Utilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Internal.ExpressionExtensions;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{
@ -24,36 +28,40 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{
private static readonly Dictionary<string, string> _convertMappings = new Dictionary<string, string>
{
{nameof(Boolean), "CBOOL"},
{nameof(Byte), "CBYTE"},
{nameof(SByte), "CINT"},
{nameof(Int16), "CINT"},
{nameof(Int32), "CLNG"},
{nameof(Single), "CSNG"},
{nameof(Double), "CDBL"},
{nameof(Decimal), "CCUR"},
{nameof(DateTime), "CDATE"},
{ nameof(Boolean), "CBOOL" },
{ nameof(Byte), "CBYTE" },
{ nameof(SByte), "CINT" },
{ nameof(Int16), "CINT" },
{ nameof(Int32), "CLNG" },
{ nameof(Single), "CSNG" },
{ nameof(Double), "CDBL" },
{ nameof(Decimal), "CCUR" },
{ nameof(DateTime), "CDATE" },
};
private readonly ITypeMappingSource _typeMappingSource;
private readonly IJetOptions _options;
private readonly ISqlGenerationHelper _sqlGenerationHelper;
//private readonly JetSqlExpressionFactory _sqlExpressionFactory;
private CoreTypeMapping _boolTypeMapping;
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public JetQuerySqlGenerator(
[NotNull] QuerySqlGeneratorDependencies dependencies,
ITypeMappingSource typeMappingSource,
[JetBrains.Annotations.NotNull] QuerySqlGeneratorDependencies dependencies,
//ISqlExpressionFactory sqlExpressionFactory,
[JetBrains.Annotations.NotNull] ITypeMappingSource typeMappingSource,
IJetOptions options)
: base(dependencies)
{
//_sqlExpressionFactory = (JetSqlExpressionFactory)sqlExpressionFactory;
_typeMappingSource = typeMappingSource;
_options = options;
_sqlGenerationHelper = dependencies.SqlGenerationHelper;
_boolTypeMapping = _typeMappingSource.FindMapping(typeof(bool));
//_jetSqlExpressionFactory = jetSqlExpressionFactory;
}
protected override Expression VisitSelect(SelectExpression selectExpression)
@ -65,7 +73,7 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
if (IsNonComposedSetOperation(selectExpression))
{
// Naked set operation
GenerateSetOperation((SetOperationBase) selectExpression.Tables[0]);
GenerateSetOperation((SetOperationBase)selectExpression.Tables[0]);
return selectExpression;
}
@ -112,7 +120,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
0,
selectExpression
.Tables
.Count(t => !(t is CrossJoinExpression || t is CrossApplyExpression)) - maxTablesWithoutBrackets)));
.Count(t => !(t is CrossJoinExpression || t is CrossApplyExpression)) -
maxTablesWithoutBrackets)));
for (var index = 0; index < selectExpression.Tables.Count; index++)
{
@ -120,13 +129,14 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
var isApplyExpression = tableExpression is CrossApplyExpression ||
tableExpression is OuterApplyExpression;
var isCrossExpression = tableExpression is CrossJoinExpression ||
tableExpression is CrossApplyExpression;
if (isApplyExpression)
{
throw new InvalidOperationException("Jet does not support APPLY statements. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
throw new InvalidOperationException(
"Jet does not support APPLY statements. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
}
if (index > 0)
@ -202,7 +212,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
&& selectExpression.Projection.Count == setOperation.Source1.Projection.Count
&& selectExpression.Projection.Select(
(pe, index) => pe.Expression is ColumnExpression column
&& string.Equals(column.Table.Alias, setOperation.Alias, StringComparison.OrdinalIgnoreCase)
&& string.Equals(column.Table.Alias, setOperation.Alias,
StringComparison.OrdinalIgnoreCase)
&& string.Equals(
column.Name, setOperation.Source1.Projection[index]
.Alias, StringComparison.OrdinalIgnoreCase))
@ -236,14 +247,14 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{
// Jet uses the value -1 as True, so ordering by a boolean expression will first list the True values
// before the False values, which is the opposite of what .NET and other DBMS do, which are using 1 as True.
if (orderingExpression.Expression.TypeMapping == _boolTypeMapping)
{
orderingExpression = new OrderingExpression(
orderingExpression.Expression,
!orderingExpression.IsAscending);
}
return base.VisitOrdering(orderingExpression);
}
@ -252,19 +263,29 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{
Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression));
/*if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
{
Visit(
_sqlExpressionFactory.Case(
new[]
{
new CaseWhenClause(
_sqlExpressionFactory.IsNull(sqlBinaryExpression.Left),
sqlBinaryExpression.Right)
},
sqlBinaryExpression.Left));
//Visit(
/*_sqlExpressionFactory.Case(
new[]
{
new CaseWhenClause(
_sqlExpressionFactory.IsNull(sqlBinaryExpression.Left),
sqlBinaryExpression.Right)
},
sqlBinaryExpression.Left));
return sqlBinaryExpression;*/
SqlConstantExpression nullcons = new SqlConstantExpression(Expression.Constant(null), RelationalTypeMapping.NullMapping);
SqlUnaryExpression isnullexp = new SqlUnaryExpression(ExpressionType.Equal, sqlBinaryExpression.Left, typeof(bool), null);
List<CaseWhenClause> whenclause = new List<CaseWhenClause>
{
new CaseWhenClause(isnullexp, sqlBinaryExpression.Right)
};
CaseExpression caseexp = new CaseExpression(whenclause, sqlBinaryExpression.Left);
Visit(caseexp);
return sqlBinaryExpression;
}*/
}
return base.VisitSqlBinary(sqlBinaryExpression);
}
@ -279,23 +300,37 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
var typeMapping = convertExpression.TypeMapping;
if (typeMapping == null)
throw new InvalidOperationException(RelationalStrings.UnsupportedType(convertExpression.Type.ShortDisplayName()));
throw new InvalidOperationException(
RelationalStrings.UnsupportedType(convertExpression.Type.ShortDisplayName()));
// We are explicitly converting to the target type (convertExpression.Type) and not the CLR type of the
// accociated type mapping. This allows for conversions on the database side (e.g. CDBL()) but handling
// of the returned value using a different (unaligned) type mapping (e.g. date/time related ones).
if (_convertMappings.TryGetValue(convertExpression.Type.Name, out var function))
{
/*Visit(
/* Visit(
_sqlExpressionFactory.NullChecked(
convertExpression.Operand,
_sqlExpressionFactory.Function(
function,
new[] {convertExpression.Operand},
new[] { convertExpression.Operand },
false,
new[] {false},
new[] { false },
typeMapping.ClrType)));
*/
SqlExpression checksqlexp = convertExpression.Operand;
SqlFunctionExpression notnullsqlexp = new SqlFunctionExpression(function, new SqlExpression[] { convertExpression.Operand },
false, new[] { false }, typeMapping.ClrType,null);
SqlConstantExpression nullcons = new SqlConstantExpression(Expression.Constant(null),RelationalTypeMapping.NullMapping);
SqlUnaryExpression isnullexp = new SqlUnaryExpression(ExpressionType.Equal, checksqlexp, typeof(bool), null);
List<CaseWhenClause> whenclause = new List<CaseWhenClause>
{
new CaseWhenClause(isnullexp, nullcons)
};
CaseExpression caseexp = new CaseExpression(whenclause, notnullsqlexp);
Visit(caseexp);
return convertExpression;
}
@ -325,7 +360,7 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
return likeExpression;
}
protected override string GetOperator([NotNull] SqlBinaryExpression binaryExpression)
protected override string GetOperator([JetBrains.Annotations.NotNull] SqlBinaryExpression binaryExpression)
=> binaryExpression.OperatorType switch
{
ExpressionType.Add when binaryExpression.Type == typeof(string) => " & ",
@ -357,7 +392,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
}
else
{
throw new InvalidOperationException("Jet does not support skipping rows. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
throw new InvalidOperationException(
"Jet does not support skipping rows. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
}
}
@ -441,7 +477,5 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
protected override Expression VisitRowNumber(RowNumberExpression rowNumberExpression)
=> throw new InvalidOperationException(CoreStrings.TranslationFailed(rowNumberExpression));
}
}
Loading…
Cancel
Save