/// <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 virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); Check.NotNull(logger, nameof(logger)); if (_memberToFunctionName.TryGetValue(member, out var functionName)) { return(returnType == typeof(bool) ? _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: false, argumentsPropagateNullability: new[] { false }, returnType)) }, null) : (SqlExpression)_sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType)); } return(null); }
/// <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 virtual SqlExpression?Translate( SqlExpression?instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); Check.NotNull(logger, nameof(logger)); if (member.DeclaringType?.IsNullableValueType() == true && instance != null) { switch (member.Name) { case nameof(Nullable <int> .Value): return(instance); case nameof(Nullable <int> .HasValue): return(_sqlExpressionFactory.IsNotNull(instance)); } } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (_memberToFunctionName.TryGetValue(member, out var functionName)) { SqlExpression translation = _sqlExpressionFactory.Function( functionName, new[] { instance }, returnType); if (returnType == typeof(bool)) { translation = _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), translation) }, null); } return(translation); } return(null); }
/// <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 virtual SqlExpression?Translate( SqlExpression?instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); Check.NotNull(logger, nameof(logger)); if (Equals(member, _isClosed) && instance != null) { return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( "IsClosed", new[] { instance }, nullable: false, argumentsPropagateNullability: new[] { false }, returnType)) }, null)); } return(null); }
private Expression VisitSqlUnaryExpression(SqlUnaryExpression sqlUnaryExpression) { if (sqlUnaryExpression.OperatorType == ExpressionType.Not) { return(VisitNot(sqlUnaryExpression)); } // NULL IS NULL -> true // non_nullablee_constant IS NULL -> false if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull1) { return(_sqlExpressionFactory.Constant(innerConstantNull1.Value == null, sqlUnaryExpression.TypeMapping)); } // NULL IS NOT NULL -> false // non_nullablee_constant IS NOT NULL -> true if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull2) { return(_sqlExpressionFactory.Constant(innerConstantNull2.Value != null, sqlUnaryExpression.TypeMapping)); } if (sqlUnaryExpression.Operand is SqlUnaryExpression innerUnary) { // (!a) IS NULL <==> a IS NULL if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand))); } // (!a) IS NOT NULL <==> a IS NOT NULL if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand))); } } var newOperand = (SqlExpression)Visit(sqlUnaryExpression.Operand); return(sqlUnaryExpression.Update(newOperand)); }
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList <SqlExpression> arguments) { method = method.OnInterface(typeof(IGeometry)); if (_methodToFunctionName.TryGetValue(method, out var functionName)) { SqlExpression translation = _sqlExpressionFactory.Function( functionName, new[] { instance }.Concat(arguments), method.ReturnType); if (method.ReturnType == typeof(bool)) { translation = _sqlExpressionFactory.Case( new[] { new CaseWhenClause(_sqlExpressionFactory.IsNotNull(instance), translation) }, null); } return(translation); } if (Equals(method, _getGeometryN)) { return(_sqlExpressionFactory.Function( "GeometryN", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)) }, method.ReturnType)); } if (Equals(method, _isWithinDistance)) { return(_sqlExpressionFactory.LessThanOrEqual( _sqlExpressionFactory.Function( "Distance", new[] { instance, arguments[0] }, typeof(double)), arguments[1])); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { if (member.DeclaringType.IsNullableType()) { switch (member.Name) { case nameof(Nullable <int> .Value): return(instance); case nameof(Nullable <int> .HasValue): return(_sqlExpressionFactory.IsNotNull(instance)); } } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { if (Equals(member, _isClosed)) { return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( "IsClosed", new[] { instance }, returnType)) }, null)); } return(null); }
/// <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 virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (Equals(member, _isClosed)) { return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( "IsClosed", new[] { instance }, nullable: false, argumentsPropagateNullability: new[] { false }, returnType)) }, null)); } return(null); }
public SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { if (_memberToFunctionName.TryGetValue(member.OnInterface(typeof(ICurve)), out var functionName)) { SqlExpression translation = _sqlExpressionFactory.Function( functionName, new[] { instance }, returnType); if (returnType == typeof(bool)) { translation = _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), translation) }, null); } return(translation); } return(null); }
private Expression VisitSqlUnaryExpression(SqlUnaryExpression sqlUnaryExpression) { // !(true) -> false // !(false) -> true if (sqlUnaryExpression.OperatorType == ExpressionType.Not && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantBool && innerConstantBool.Value is bool value) { return(value ? _sqlExpressionFactory.Constant(false, sqlUnaryExpression.TypeMapping) : _sqlExpressionFactory.Constant(true, sqlUnaryExpression.TypeMapping)); } // NULL IS NULL -> true // non_nullablee_constant IS NULL -> false if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull1) { return(_sqlExpressionFactory.Constant(innerConstantNull1.Value == null, sqlUnaryExpression.TypeMapping)); } // NULL IS NOT NULL -> false // non_nullablee_constant IS NOT NULL -> true if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull2) { return(_sqlExpressionFactory.Constant(innerConstantNull2.Value != null, sqlUnaryExpression.TypeMapping)); } if (sqlUnaryExpression.Operand is SqlUnaryExpression innerUnary) { if (sqlUnaryExpression.OperatorType == ExpressionType.Not) { // !(!a) -> a if (innerUnary.OperatorType == ExpressionType.Not) { return(Visit(innerUnary.Operand)); } if (innerUnary.OperatorType == ExpressionType.Equal) { //!(a IS NULL) -> a IS NOT NULL return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand))); } //!(a IS NOT NULL) -> a IS NULL if (innerUnary.OperatorType == ExpressionType.NotEqual) { return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand))); } } // (!a) IS NULL <==> a IS NULL if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand))); } // (!a) IS NOT NULL <==> a IS NOT NULL if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand))); } } if (sqlUnaryExpression.Operand is SqlBinaryExpression innerBinary) { // De Morgan's if (innerBinary.OperatorType == ExpressionType.AndAlso || innerBinary.OperatorType == ExpressionType.OrElse) { var newLeft = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Left)); var newRight = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Right)); return(innerBinary.OperatorType == ExpressionType.AndAlso ? _sqlExpressionFactory.OrElse(newLeft, newRight) : _sqlExpressionFactory.AndAlso(newLeft, newRight)); } // note that those optimizations are only valid in 2-value logic // they are safe to do here because null semantics removes possibility of nulls in the tree // however if we decide to do "partial" null semantics (that doesn't distinguish between NULL and FALSE, e.g. for predicates) // we need to be extra careful here if (TryNegate(innerBinary.OperatorType, out var negated)) { return(Visit( _sqlExpressionFactory.MakeBinary( negated, innerBinary.Left, innerBinary.Right, innerBinary.TypeMapping))); } } var newOperand = (SqlExpression)Visit(sqlUnaryExpression.Operand); return(sqlUnaryExpression.Update(newOperand)); }
/// <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 virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (_memberToFunctionName.TryGetValue(member, out var functionName)) { return(returnType == typeof(bool) ? _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: false, argumentsPropagateNullability: new[] { false }, returnType)) }, null) : (SqlExpression)_sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType)); } if (Equals(member, _geometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType), _sqlExpressionFactory.Constant(" ZM") }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType), new[] { new CaseWhenClause(_sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant("Point")), new CaseWhenClause(_sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant("LineString")), new CaseWhenClause(_sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant("Polygon")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant("MultiPoint")), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant("MultiLineString")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant("MultiPolygon")), new CaseWhenClause( _sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant("GeometryCollection")) }, null)); } if (Equals(member, _ogcGeometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(string)), _sqlExpressionFactory.Constant(" ZM") }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(string)), new[] { new CaseWhenClause(_sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant(OgcGeometryType.Point)), new CaseWhenClause( _sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant(OgcGeometryType.LineString)), new CaseWhenClause(_sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant(OgcGeometryType.Polygon)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPoint)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiLineString)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPolygon)), new CaseWhenClause( _sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant(OgcGeometryType.GeometryCollection)) }, null)); } return(null); }
public SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { member = member.OnInterface(typeof(IGeometry)); if (_memberToFunctionName.TryGetValue(member, out var functionName)) { SqlExpression translation = _sqlExpressionFactory.Function(functionName, new[] { instance }, returnType); if (returnType == typeof(bool)) { translation = _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), translation) }, null); } return(translation); } if (Equals(member, _geometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new [] { instance, }, returnType), _sqlExpressionFactory.Constant(" ZM") }, returnType), new CaseWhenClause(_sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant("Point")), new CaseWhenClause(_sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant("LineString")), new CaseWhenClause(_sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant("Polygon")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant("MultiPoint")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant("MultiLineString")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant("MultiPolygon")), new CaseWhenClause(_sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant("GeometryCollection")))); } if (Equals(member, _ogcGeometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new [] { instance, }, typeof(string)), _sqlExpressionFactory.Constant(" ZM") }, typeof(string)), new CaseWhenClause(_sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant(OgcGeometryType.Point)), new CaseWhenClause(_sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant(OgcGeometryType.LineString)), new CaseWhenClause(_sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant(OgcGeometryType.Polygon)), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPoint)), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiLineString)), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPolygon)), new CaseWhenClause(_sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant(OgcGeometryType.GeometryCollection)))); } return(null); }
private SqlExpression SimplifyUnaryExpression( ExpressionType operatorType, SqlExpression operand, Type type, RelationalTypeMapping typeMapping) { switch (operatorType) { case ExpressionType.Not when type == typeof(bool): { switch (operand) { // !(true) -> false // !(false) -> true case SqlConstantExpression constantOperand when constantOperand.Value is bool value: { return(_sqlExpressionFactory.Constant(!value, typeMapping)); } case InExpression inOperand: return(inOperand.Negate()); case SqlUnaryExpression unaryOperand: switch (unaryOperand.OperatorType) { // !(!a) -> a case ExpressionType.Not: return(unaryOperand.Operand); //!(a IS NULL) -> a IS NOT NULL case ExpressionType.Equal: return(_sqlExpressionFactory.IsNotNull(unaryOperand.Operand)); //!(a IS NOT NULL) -> a IS NULL case ExpressionType.NotEqual: return(_sqlExpressionFactory.IsNull(unaryOperand.Operand)); } break; case SqlBinaryExpression binaryOperand: { // De Morgan's if (binaryOperand.OperatorType == ExpressionType.AndAlso || binaryOperand.OperatorType == ExpressionType.OrElse) { var newLeft = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Left, type, typeMapping); var newRight = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Right, type, typeMapping); return(SimplifyLogicalSqlBinaryExpression( binaryOperand.OperatorType == ExpressionType.AndAlso ? ExpressionType.OrElse : ExpressionType.AndAlso, newLeft, newRight, binaryOperand.TypeMapping !)); } // those optimizations are only valid in 2-value logic // they are safe to do here because if we apply null semantics // because null semantics removes possibility of nulls in the tree when the comparison is wrapped around NOT if (!_useRelationalNulls && TryNegate(binaryOperand.OperatorType, out var negated)) { return(SimplifyBinaryExpression( negated, binaryOperand.Left, binaryOperand.Right, binaryOperand.TypeMapping !)); } } break; } break; } case ExpressionType.Equal: case ExpressionType.NotEqual: return(SimplifyNullNotNullExpression( operatorType, operand, type, typeMapping)); } return(_sqlExpressionFactory.MakeUnary(operatorType, operand, type, typeMapping) !); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); if (_methodToFunctionName.TryGetValue(method, out var functionName)) { var finalArguments = new[] { instance }.Concat(arguments); if (method.ReturnType == typeof(bool)) { var nullCheck = (SqlExpression)_sqlExpressionFactory.IsNotNull(instance); foreach (var argument in arguments) { nullCheck = _sqlExpressionFactory.AndAlso( nullCheck, _sqlExpressionFactory.IsNotNull(argument)); } return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( nullCheck, _sqlExpressionFactory.Function( functionName, finalArguments, nullResultAllowed: false, finalArguments.Select(a => false), method.ReturnType)) }, null)); } return(_sqlExpressionFactory.Function( functionName, finalArguments, nullResultAllowed: true, finalArguments.Select(a => true), method.ReturnType)); } if (Equals(method, _getGeometryN)) { return(_sqlExpressionFactory.Function( "GeometryN", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)) }, nullResultAllowed: true, argumentsPropagateNullability: new[] { true, true }, method.ReturnType)); } if (Equals(method, _isWithinDistance)) { return(_sqlExpressionFactory.LessThanOrEqual( _sqlExpressionFactory.Function( "Distance", new[] { instance, arguments[0] }, nullResultAllowed: true, argumentsPropagateNullability: new[] { true, true }, typeof(double)), arguments[1])); } return(null); }
/// <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 virtual SqlExpression?Translate( SqlExpression?instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (instance != null) { if (MethodToFunctionName.TryGetValue(method, out var functionName)) { var finalArguments = new[] { instance }.Concat(arguments); if (method.ReturnType == typeof(bool)) { var nullCheck = (SqlExpression)_sqlExpressionFactory.IsNotNull(instance); foreach (var argument in arguments) { nullCheck = _sqlExpressionFactory.AndAlso( nullCheck, _sqlExpressionFactory.IsNotNull(argument)); } return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( nullCheck, _sqlExpressionFactory.Function( functionName, finalArguments, nullable: false, finalArguments.Select(a => false), method.ReturnType)) }, null)); } return(_sqlExpressionFactory.Function( functionName, finalArguments, nullable: true, finalArguments.Select(a => true), method.ReturnType)); } if (Equals(method, GetGeometryN)) { return(_sqlExpressionFactory.Function( "GeometryN", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)) }, nullable: true, argumentsPropagateNullability: new[] { true, true }, method.ReturnType)); } if (Equals(method, IsWithinDistance)) { return(_sqlExpressionFactory.LessThanOrEqual( _sqlExpressionFactory.Function( "Distance", new[] { instance, arguments[0] }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(double)), arguments[1])); } } return(null); }
/// <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 virtual SqlExpression?Translate( SqlExpression?instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (instance != null) { if (MemberToFunctionName.TryGetValue(member, out var functionName)) { return(returnType == typeof(bool) ? _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(instance), _sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: false, argumentsPropagateNullability: new[] { false }, returnType)) }, null) : _sqlExpressionFactory.Function( functionName, new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType)); } if (Equals(member, GeometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType), _sqlExpressionFactory.Constant(" ZM") }, nullable: true, argumentsPropagateNullability: new[] { true }, returnType), new[] { new CaseWhenClause(_sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant("Point")), new CaseWhenClause(_sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant("LineString")), new CaseWhenClause(_sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant("Polygon")), new CaseWhenClause(_sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant("MultiPoint")), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant("MultiLineString")), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant("MultiPolygon")), new CaseWhenClause( _sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant("GeometryCollection")) }, null)); } if (Equals(member, OgcGeometryType)) { return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "GeometryType", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(string)), _sqlExpressionFactory.Constant(" ZM") }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(string)), new[] { new CaseWhenClause( _sqlExpressionFactory.Constant("POINT"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.Point)), new CaseWhenClause( _sqlExpressionFactory.Constant("LINESTRING"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.LineString)), new CaseWhenClause( _sqlExpressionFactory.Constant("POLYGON"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.Polygon)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTIPOINT"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.MultiPoint)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTILINESTRING"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.MultiLineString)), new CaseWhenClause( _sqlExpressionFactory.Constant("MULTIPOLYGON"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.MultiPolygon)), new CaseWhenClause( _sqlExpressionFactory.Constant("GEOMETRYCOLLECTION"), _sqlExpressionFactory.Constant(NetTopologySuite.Geometries.OgcGeometryType.GeometryCollection)) }, null)); } } return(null); }
public override Expression Visit(Expression expression) { if (expression is InExpression inExpression && inExpression.Values != null) { var inValues = new List <object>(); var hasNullValue = false; RelationalTypeMapping typeMapping = null; switch (inExpression.Values) { case SqlConstantExpression sqlConstant: { typeMapping = sqlConstant.TypeMapping; var values = (IEnumerable)sqlConstant.Value; foreach (var value in values) { if (value == null) { hasNullValue = true; continue; } inValues.Add(value); } } break; case SqlParameterExpression sqlParameter: { typeMapping = sqlParameter.TypeMapping; var values = (IEnumerable)_parametersValues[sqlParameter.Name]; foreach (var value in values) { if (value == null) { hasNullValue = true; continue; } inValues.Add(value); } } break; } var updatedInExpression = inValues.Count > 0 ? _sqlExpressionFactory.In( (SqlExpression)Visit(inExpression.Item), _sqlExpressionFactory.Constant(inValues, typeMapping), inExpression.Negated) : null; var nullCheckExpression = hasNullValue ? inExpression.Negated ? _sqlExpressionFactory.IsNotNull(inExpression.Item) : _sqlExpressionFactory.IsNull(inExpression.Item) : null; if (updatedInExpression != null && nullCheckExpression != null) { return(inExpression.Negated ? _sqlExpressionFactory.AndAlso(updatedInExpression, nullCheckExpression) : _sqlExpressionFactory.OrElse(updatedInExpression, nullCheckExpression)); } if (updatedInExpression == null && nullCheckExpression == null) { return(_sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(inExpression.Negated))); } return((SqlExpression)updatedInExpression ?? nullCheckExpression); } return(base.Visit(expression)); }
protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression) { Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression)); _isNullable = false; var canOptimize = _canOptimize; // for SqlServer we could also allow optimize on children of ExpressionType.Equal // because they get converted to CASE blocks anyway, but for other providers it's incorrect // once/if null semantics optimizations are provider-specific we can enable it _canOptimize = _canOptimize && (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso || sqlBinaryExpression.OperatorType == ExpressionType.OrElse); var nonNullableColumns = new List <ColumnExpression>(); if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso) { nonNullableColumns = FindNonNullableColumns(sqlBinaryExpression.Left); } var newLeft = (SqlExpression)Visit(sqlBinaryExpression.Left); var leftNullable = _isNullable; _isNullable = false; if (nonNullableColumns.Count > 0) { _nonNullableColumns.AddRange(nonNullableColumns); } var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right); var rightNullable = _isNullable; foreach (var nonNullableColumn in nonNullableColumns) { _nonNullableColumns.Remove(nonNullableColumn); } if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce) { _isNullable = leftNullable && rightNullable; _canOptimize = canOptimize; return(sqlBinaryExpression.Update(newLeft, newRight)); } if (sqlBinaryExpression.OperatorType == ExpressionType.Add && sqlBinaryExpression.Type == typeof(string)) { if (leftNullable) { newLeft = newLeft is SqlConstantExpression ? _sqlExpressionFactory.Constant(string.Empty) : newLeft is ColumnExpression || newLeft is SqlParameterExpression ? _sqlExpressionFactory.Coalesce(newLeft, _sqlExpressionFactory.Constant(string.Empty)) : newLeft; } if (rightNullable) { newRight = newRight is SqlConstantExpression ? _sqlExpressionFactory.Constant(string.Empty) : newRight is ColumnExpression || newRight is SqlParameterExpression ? _sqlExpressionFactory.Coalesce(newRight, _sqlExpressionFactory.Constant(string.Empty)) : newRight; } return(sqlBinaryExpression.Update(newLeft, newRight)); } if (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual) { var leftConstantNull = newLeft is SqlConstantExpression leftConstant && leftConstant.Value == null; var rightConstantNull = newRight is SqlConstantExpression rightConstant && rightConstant.Value == null; // a == null -> a IS NULL // a != null -> a IS NOT NULL if (rightConstantNull) { _isNullable = false; _canOptimize = canOptimize; return(sqlBinaryExpression.OperatorType == ExpressionType.Equal ? _sqlExpressionFactory.IsNull(newLeft) : _sqlExpressionFactory.IsNotNull(newLeft)); } // null == a -> a IS NULL // null != a -> a IS NOT NULL if (leftConstantNull) { _isNullable = false; _canOptimize = canOptimize; return(sqlBinaryExpression.OperatorType == ExpressionType.Equal ? _sqlExpressionFactory.IsNull(newRight) : _sqlExpressionFactory.IsNotNull(newRight)); } var leftUnary = newLeft as SqlUnaryExpression; var rightUnary = newRight as SqlUnaryExpression; var leftNegated = leftUnary?.IsLogicalNot() == true; var rightNegated = rightUnary?.IsLogicalNot() == true; if (leftNegated) { newLeft = leftUnary.Operand; } if (rightNegated) { newRight = rightUnary.Operand; } var leftIsNull = _sqlExpressionFactory.IsNull(newLeft); var rightIsNull = _sqlExpressionFactory.IsNull(newRight); // optimized expansion which doesn't distinguish between null and false if (canOptimize && sqlBinaryExpression.OperatorType == ExpressionType.Equal && !leftNegated && !rightNegated) { // when we use optimized form, the result can still be nullable if (leftNullable && rightNullable) { _isNullable = true; _canOptimize = canOptimize; return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.Equal(newLeft, newRight), _sqlExpressionFactory.AndAlso(leftIsNull, rightIsNull))); } if ((leftNullable && !rightNullable) || (!leftNullable && rightNullable)) { _isNullable = true; _canOptimize = canOptimize; return(_sqlExpressionFactory.Equal(newLeft, newRight)); } } // doing a full null semantics rewrite - removing all nulls from truth table // this will NOT be correct once we introduce simplified null semantics _isNullable = false; _canOptimize = canOptimize; if (sqlBinaryExpression.OperatorType == ExpressionType.Equal) { if (!leftNullable && !rightNullable) { // a == b <=> !a == !b -> a == b // !a == b <=> a == !b -> a != b return(leftNegated == rightNegated ? _sqlExpressionFactory.Equal(newLeft, newRight) : _sqlExpressionFactory.NotEqual(newLeft, newRight)); } if (leftNullable && rightNullable) { // ?a == ?b <=> !(?a) == !(?b) -> [(a == b) && (a != null && b != null)] || (a == null && b == null)) // !(?a) == ?b <=> ?a == !(?b) -> [(a != b) && (a != null && b != null)] || (a == null && b == null) return(leftNegated == rightNegated ? ExpandNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull) : ExpandNegatedNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)); } if (leftNullable && !rightNullable) { // ?a == b <=> !(?a) == !b -> (a == b) && (a != null) // !(?a) == b <=> ?a == !b -> (a != b) && (a != null) return(leftNegated == rightNegated ? ExpandNullableEqualNonNullable(newLeft, newRight, leftIsNull) : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, leftIsNull)); } if (rightNullable && !leftNullable) { // a == ?b <=> !a == !(?b) -> (a == b) && (b != null) // !a == ?b <=> a == !(?b) -> (a != b) && (b != null) return(leftNegated == rightNegated ? ExpandNullableEqualNonNullable(newLeft, newRight, rightIsNull) : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, rightIsNull)); } } if (sqlBinaryExpression.OperatorType == ExpressionType.NotEqual) { if (!leftNullable && !rightNullable) { // a != b <=> !a != !b -> a != b // !a != b <=> a != !b -> a == b return(leftNegated == rightNegated ? _sqlExpressionFactory.NotEqual(newLeft, newRight) : _sqlExpressionFactory.Equal(newLeft, newRight)); } if (leftNullable && rightNullable) { // ?a != ?b <=> !(?a) != !(?b) -> [(a != b) || (a == null || b == null)] && (a != null || b != null) // !(?a) != ?b <=> ?a != !(?b) -> [(a == b) || (a == null || b == null)] && (a != null || b != null) return(leftNegated == rightNegated ? ExpandNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull) : ExpandNegatedNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)); } if (leftNullable) { // ?a != b <=> !(?a) != !b -> (a != b) || (a == null) // !(?a) != b <=> ?a != !b -> (a == b) || (a == null) return(leftNegated == rightNegated ? ExpandNullableNotEqualNonNullable(newLeft, newRight, leftIsNull) : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, leftIsNull)); } if (rightNullable) { // a != ?b <=> !a != !(?b) -> (a != b) || (b == null) // !a != ?b <=> a != !(?b) -> (a == b) || (b == null) return(leftNegated == rightNegated ? ExpandNullableNotEqualNonNullable(newLeft, newRight, rightIsNull) : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, rightIsNull)); } } } _isNullable = leftNullable || rightNullable; _canOptimize = canOptimize; return(sqlBinaryExpression.Update(newLeft, newRight)); }