From e04ee43a294aef7f0f4e4eb9f5ca7006ad74045d Mon Sep 17 00:00:00 2001 From: Christopher Jolly Date: Mon, 2 Oct 2023 23:51:14 +0800 Subject: [PATCH] UNIONing projection with a numeric column to another projection that specifically projects NULL the the same name as a numeric field, the JOINING that to another table can leave that numeric field as another data type (NULL is not a numeric data type). Hence when using that supposedly numeric field in the join predicat against another numeric field we get a data type mismatch. We try to detect this and when visiting the column (but only if we are within a binary expression and not a projection) we quickly wrap it in a convert function to make it properly numeric The top/outer projection of this field can still be in a non numeric format, but JetDataReader is able to handle that. Would be better to produce the correctly converted data output anyway --- .../Sql/Internal/JetQuerySqlGenerator.cs | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs b/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs index de6782f..8421a37 100644 --- a/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs +++ b/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs @@ -46,6 +46,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal private readonly ISqlGenerationHelper _sqlGenerationHelper; //private readonly JetSqlExpressionFactory _sqlExpressionFactory; + private List _nullNumerics = new List(); + private Stack parent = new Stack(); private CoreTypeMapping? _boolTypeMapping; /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -319,9 +321,30 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal return result; } - protected override Expression VisitInnerJoin(InnerJoinExpression innerJoinExpression) + protected override Expression VisitProjection(ProjectionExpression projectionExpression) { - return base.VisitInnerJoin(innerJoinExpression); + if (projectionExpression.Expression is SqlConstantExpression { Value: null } constantExpression && (constantExpression.Type == typeof(int) || constantExpression.Type == typeof(double) || constantExpression.Type == typeof(float) || constantExpression.Type == typeof(decimal) || constantExpression.Type == typeof(short))) + { + _nullNumerics.Add(projectionExpression.Alias); + } + return base.VisitProjection(projectionExpression); + } + + protected override Expression VisitColumn(ColumnExpression columnExpression) + { + if (columnExpression.IsNullable && _nullNumerics.Contains(columnExpression.Name) && _convertMappings.TryGetValue(columnExpression.Type.Name, out var function)) + { + + if (parent.TryPeek(out var exp) && exp is SqlBinaryExpression) + { + Sql.Append(function); + Sql.Append("("); + base.VisitColumn(columnExpression); + Sql.Append(")"); + return columnExpression; + } + } + return base.VisitColumn(columnExpression); } private bool IsNonComposedSetOperation(SelectExpression selectExpression) @@ -415,8 +438,10 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal Visit(caseexp); return sqlBinaryExpression; } - - return base.VisitSqlBinary(sqlBinaryExpression); + parent.Push(sqlBinaryExpression); + var res = base.VisitSqlBinary(sqlBinaryExpression); + parent.Pop(); + return res; } protected override Expression VisitIn(InExpression inExpression)