@ -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 ) ) ;
}
}