/// <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 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 }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(string)), _sqlExpressionFactory.Constant(" ZM") }, nullable: true, argumentsPropagateNullability: new[] { true }, 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); }
/// <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) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); Check.NotNull(logger, nameof(logger)); 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); }
public override Expression Visit(Expression expression) { if (expression is InExpression inExpression) { var inValues = new List<object>(); var hasNullValue = false; CoreTypeMapping 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 ? _sqlExpressionFactory.IsNull(inExpression.Item) : null; if (updatedInExpression != null && nullCheckExpression != null) { return _sqlExpressionFactory.OrElse(updatedInExpression, nullCheckExpression); } if (updatedInExpression == null && nullCheckExpression == null) { return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(false)); } return (SqlExpression)updatedInExpression ?? nullCheckExpression; } return base.Visit(expression); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (member.DeclaringType == typeof(DateTime)) { var memberName = member.Name; if (_datePartMapping.TryGetValue(memberName, out var datePart)) { return(_sqlExpressionFactory.Convert( TaosExpression.Strftime( _sqlExpressionFactory, typeof(string), datePart, instance), returnType)); } if (string.Equals(memberName, nameof(DateTime.Ticks))) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.Multiply( _sqlExpressionFactory.Subtract( _sqlExpressionFactory.Function( "julianday", new[] { instance }, true, null, typeof(double)), _sqlExpressionFactory.Constant(1721425.5)), // NB: Result of julianday('0001-01-01 00:00:00') _sqlExpressionFactory.Constant(TimeSpan.TicksPerDay)), typeof(long))); } if (string.Equals(memberName, nameof(DateTime.Millisecond))) { return(_sqlExpressionFactory.Modulo( _sqlExpressionFactory.Multiply( _sqlExpressionFactory.Convert( TaosExpression.Strftime( _sqlExpressionFactory, typeof(string), "%f", instance), typeof(double)), _sqlExpressionFactory.Constant(1000)), _sqlExpressionFactory.Constant(1000))); } var format = "%Y-%m-%d %H:%M:%f"; SqlExpression timestring; var modifiers = new List <SqlExpression>(); switch (memberName) { case nameof(DateTime.Now): timestring = _sqlExpressionFactory.Constant("now"); modifiers.Add(_sqlExpressionFactory.Constant("localtime")); break; case nameof(DateTime.UtcNow): timestring = _sqlExpressionFactory.Constant("now"); break; case nameof(DateTime.Date): timestring = instance; modifiers.Add(_sqlExpressionFactory.Constant("start of day")); break; case nameof(DateTime.Today): timestring = _sqlExpressionFactory.Constant("now"); modifiers.Add(_sqlExpressionFactory.Constant("localtime")); modifiers.Add(_sqlExpressionFactory.Constant("start of day")); break; case nameof(DateTime.TimeOfDay): format = "%H:%M:%f"; timestring = instance; break; default: return(null); } Debug.Assert(timestring != null); return(_sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { TaosExpression.Strftime( _sqlExpressionFactory, returnType, format, timestring, modifiers), _sqlExpressionFactory.Constant("0") }, true, null, returnType), _sqlExpressionFactory.Constant(".") }, true, null, 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 override Expression Visit(Expression expression) { if (expression is FromSqlExpression fromSql) { if (!_visitedFromSqlExpressions.TryGetValue(fromSql, out var updatedFromSql)) { switch (fromSql.Arguments) { case ParameterExpression parameterExpression: var parameterValues = (object[])_parametersValues[parameterExpression.Name]; _canCache = false; var subParameters = new List <IRelationalParameter>(parameterValues.Length); // ReSharper disable once ForCanBeConvertedToForeach for (var i = 0; i < parameterValues.Length; i++) { var parameterName = _parameterNameGenerator.GenerateNext(); if (parameterValues[i] is DbParameter dbParameter) { if (string.IsNullOrEmpty(dbParameter.ParameterName)) { dbParameter.ParameterName = parameterName; } else { parameterName = dbParameter.ParameterName; } subParameters.Add(new RawRelationalParameter(parameterName, dbParameter)); } else { subParameters.Add( new TypeMappedRelationalParameter( parameterName, parameterName, _typeMappingSource.GetMappingForValue(parameterValues[i]), parameterValues[i]?.GetType().IsNullableType())); } } updatedFromSql = fromSql.Update( Expression.Constant(new CompositeRelationalParameter(parameterExpression.Name, subParameters))); _visitedFromSqlExpressions[fromSql] = updatedFromSql; break; case ConstantExpression constantExpression: var existingValues = (object[])constantExpression.Value; var constantValues = new object[existingValues.Length]; for (var i = 0; i < existingValues.Length; i++) { var value = existingValues[i]; if (value is DbParameter dbParameter) { var parameterName = _parameterNameGenerator.GenerateNext(); if (string.IsNullOrEmpty(dbParameter.ParameterName)) { dbParameter.ParameterName = parameterName; } else { parameterName = dbParameter.ParameterName; } constantValues[i] = new RawRelationalParameter(parameterName, dbParameter); } else { constantValues[i] = _sqlExpressionFactory.Constant( value, _typeMappingSource.GetMappingForValue(value)); } } updatedFromSql = fromSql.Update(Expression.Constant(constantValues, typeof(object[]))); _visitedFromSqlExpressions[fromSql] = updatedFromSql; break; } } return(updatedFromSql); } return(base.Visit(expression)); }
public virtual SqlExpression?Translate( SqlExpression?instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); Check.NotNull(logger, nameof(logger)); if (instance != null) { if (_containsMethodInfo.Equals(method)) { return(TranslateSystemFunction("CONTAINS", instance, arguments[0], typeof(bool))); } if (_startsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("STARTSWITH", instance, arguments[0], typeof(bool))); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("ENDSWITH", instance, arguments[0], typeof(bool))); } } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("LEFT", arguments[0], _sqlExpressionFactory.Constant(1), typeof(char))); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("RIGHT", arguments[0], _sqlExpressionFactory.Constant(1), typeof(char))); } if (_stringConcatWithTwoArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], arguments[1])); } if (_stringConcatWithThreeArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], arguments[2]))); } if (_stringConcatWithFourArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], _sqlExpressionFactory.Add( arguments[2], arguments[3])))); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); SqlExpression modifier = null; if (_addMilliseconds.Equals(method)) { modifier = _sqlExpressionFactory.Add( _sqlExpressionFactory.Convert( _sqlExpressionFactory.Divide( arguments[0], _sqlExpressionFactory.Constant(1000.0)), typeof(string)), _sqlExpressionFactory.Constant(" seconds")); } else if (_addTicks.Equals(method)) { modifier = _sqlExpressionFactory.Add( _sqlExpressionFactory.Convert( _sqlExpressionFactory.Divide( arguments[0], _sqlExpressionFactory.Constant((double)TimeSpan.TicksPerDay)), typeof(string)), _sqlExpressionFactory.Constant(" seconds")); } else if (_methodInfoToUnitSuffix.TryGetValue(method, out var unitSuffix)) { modifier = _sqlExpressionFactory.Add( _sqlExpressionFactory.Convert(arguments[0], typeof(string)), _sqlExpressionFactory.Constant(unitSuffix)); } if (modifier != null) { return(_sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { SqliteExpression.Strftime( _sqlExpressionFactory, method.ReturnType, "%Y-%m-%d %H:%M:%f", instance, new[] { modifier }), _sqlExpressionFactory.Constant("0") }, nullResultAllowed: true, argumentsPropagateNullability: new[] { true, false }, method.ReturnType), _sqlExpressionFactory.Constant(".") }, nullResultAllowed: true, argumentsPropagateNullability: new[] { true, false }, method.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, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); if (_indexOfMethodInfo.Equals(method)) { var argument = arguments[0]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, argument); argument = _sqlExpressionFactory.ApplyTypeMapping(argument, stringTypeMapping); SqlExpression charIndexExpression; var storeType = stringTypeMapping.StoreType; if (string.Equals(storeType, "nvarchar(max)", StringComparison.OrdinalIgnoreCase) || string.Equals(storeType, "varchar(max)", StringComparison.OrdinalIgnoreCase)) { charIndexExpression = _sqlExpressionFactory.Function( "CHARINDEX", new[] { argument, _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(long)); charIndexExpression = _sqlExpressionFactory.Convert(charIndexExpression, typeof(int)); } else { charIndexExpression = _sqlExpressionFactory.Function( "CHARINDEX", new[] { argument, _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true, true }, method.ReturnType); } charIndexExpression = _sqlExpressionFactory.Subtract(charIndexExpression, _sqlExpressionFactory.Constant(1)); return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.Equal( argument, _sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)), _sqlExpressionFactory.Constant(0)) }, charIndexExpression)); } if (_replaceMethodInfo.Equals(method)) { var firstArgument = arguments[0]; var secondArgument = arguments[1]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, firstArgument, secondArgument); instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping); firstArgument = _sqlExpressionFactory.ApplyTypeMapping(firstArgument, stringTypeMapping); secondArgument = _sqlExpressionFactory.ApplyTypeMapping(secondArgument, stringTypeMapping); return(_sqlExpressionFactory.Function( "REPLACE", new[] { instance, firstArgument, secondArgument }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType, stringTypeMapping)); } if (_toLowerMethodInfo.Equals(method) || _toUpperMethodInfo.Equals(method)) { return(_sqlExpressionFactory.Function( _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, method.ReturnType, instance.TypeMapping)); } if (_substringMethodInfo.Equals(method)) { return(_sqlExpressionFactory.Function( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType, instance.TypeMapping)); } if (_isNullOrWhiteSpaceMethodInfo.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(argument), _sqlExpressionFactory.Equal( _sqlExpressionFactory.Function( "LTRIM", new[] { _sqlExpressionFactory.Function( "RTRIM", new[] { argument }, nullable: true, argumentsPropagateNullability: new[] { true }, argument.Type, argument.TypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true }, argument.Type, argument.TypeMapping), _sqlExpressionFactory.Constant(string.Empty, argument.TypeMapping)))); } if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true || (_trimStartMethodInfoWithCharArrayArg.Equals(method) // SqlServer LTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(_sqlExpressionFactory.Function( "LTRIM", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, instance.Type, instance.TypeMapping)); } if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true || (_trimEndMethodInfoWithCharArrayArg.Equals(method) // SqlServer RTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(_sqlExpressionFactory.Function( "RTRIM", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, instance.Type, instance.TypeMapping)); } if (_trimMethodInfoWithoutArgs?.Equals(method) == true || (_trimMethodInfoWithCharArrayArg.Equals(method) // SqlServer LTRIM/RTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(_sqlExpressionFactory.Function( "LTRIM", new[] { _sqlExpressionFactory.Function( "RTRIM", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, instance.Type, instance.TypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true }, instance.Type, instance.TypeMapping)); } if (_containsMethodInfo.Equals(method)) { var pattern = arguments[0]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, pattern); instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping); pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping); if (pattern is SqlConstantExpression constantPattern) { // Intentionally string.Empty since we don't want to match nulls here. #pragma warning disable CA1820 // Test for empty strings using string length if ((string)constantPattern.Value == string.Empty) #pragma warning restore CA1820 // Test for empty strings using string length { return(_sqlExpressionFactory.Constant(true)); } return(_sqlExpressionFactory.GreaterThan( _sqlExpressionFactory.Function( "CHARINDEX", new[] { pattern, instance }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(int)), _sqlExpressionFactory.Constant(0))); } return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.Equal( pattern, _sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)), _sqlExpressionFactory.GreaterThan( _sqlExpressionFactory.Function( "CHARINDEX", new[] { pattern, instance }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(int)), _sqlExpressionFactory.Constant(0)))); } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.Function( "SUBSTRING", new[] { argument, _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType)); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.Function( "SUBSTRING", new[] { argument, _sqlExpressionFactory.Function( "LEN", new[] { argument }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(int)), _sqlExpressionFactory.Constant(1) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType)); } if (_startsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], true)); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], false)); } 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) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); Check.NotNull(logger, nameof(logger)); if (instance != null) { if (_containsMethodInfo.Equals(method)) { return(TranslateSystemFunction("CONTAINS", typeof(bool), instance, arguments[0])); } if (_startsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("STARTSWITH", typeof(bool), instance, arguments[0])); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("ENDSWITH", typeof(bool), instance, arguments[0])); } if (_toLowerMethodInfo.Equals(method)) { return(TranslateSystemFunction("LOWER", method.ReturnType, instance)); } if (_toUpperMethodInfo.Equals(method)) { return(TranslateSystemFunction("UPPER", method.ReturnType, instance)); } if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true || (_trimStartMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB LTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("LTRIM", method.ReturnType, instance)); } if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true || (_trimEndMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB RTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("RTRIM", method.ReturnType, instance)); } if (_trimMethodInfoWithoutArgs?.Equals(method) == true || (_trimMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB TRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("TRIM", method.ReturnType, instance)); } if (_substringMethodInfo.Equals(method)) { return(TranslateSystemFunction("SUBSTRING", method.ReturnType, instance, arguments[0], arguments[1])); } } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("LEFT", typeof(char), arguments[0], _sqlExpressionFactory.Constant(1))); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("RIGHT", typeof(char), arguments[0], _sqlExpressionFactory.Constant(1))); } if (_stringConcatWithTwoArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], arguments[1])); } if (_stringConcatWithThreeArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], arguments[2]))); } if (_stringConcatWithFourArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], _sqlExpressionFactory.Add( arguments[2], arguments[3])))); } 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 (typeof(Geometry).IsAssignableFrom(member.DeclaringType)) { Check.DebugAssert(instance.TypeMapping != null, "Instance must have typeMapping assigned."); var storeType = instance.TypeMapping.StoreType; var isGeography = string.Equals(storeType, "geography", StringComparison.OrdinalIgnoreCase); if (_memberToFunctionName.TryGetValue(member, out var functionName) || (!isGeography && _geometryMemberToFunctionName.TryGetValue(member, out functionName))) { var resultTypeMapping = typeof(Geometry).IsAssignableFrom(returnType) ? _typeMappingSource.FindMapping(returnType, storeType) : _typeMappingSource.FindMapping(returnType); return(_sqlExpressionFactory.Function( instance, functionName, Array.Empty <SqlExpression>(), nullable: true, instancePropagatesNullability: true, argumentsPropagateNullability: Array.Empty <bool>(), returnType, resultTypeMapping)); } if (Equals(member, _ogcGeometryType)) { var whenClauses = new List <CaseWhenClause> { 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)), new CaseWhenClause( _sqlExpressionFactory.Constant("CircularString"), _sqlExpressionFactory.Constant(OgcGeometryType.CircularString)), new CaseWhenClause( _sqlExpressionFactory.Constant("CompoundCurve"), _sqlExpressionFactory.Constant(OgcGeometryType.CompoundCurve)), new CaseWhenClause( _sqlExpressionFactory.Constant("CurvePolygon"), _sqlExpressionFactory.Constant(OgcGeometryType.CurvePolygon)) }; if (isGeography) { whenClauses.Add( new CaseWhenClause( _sqlExpressionFactory.Constant("FullGlobe"), _sqlExpressionFactory.Constant((OgcGeometryType)126))); } return(_sqlExpressionFactory.Case( _sqlExpressionFactory.Function( instance, "STGeometryType", Array.Empty <SqlExpression>(), nullable: true, instancePropagatesNullability: true, argumentsPropagateNullability: Array.Empty <bool>(), typeof(string)), whenClauses.ToArray())); } if (Equals(member, _srid)) { return(_sqlExpressionFactory.Function( instance, "STSrid", nullable: true, instancePropagatesNullability: true, returnType)); } } return(null); }
internal static SqlExpression Strftime(this ISqlExpressionFactory factory, Type returnType, string format, SqlExpression timestring, IEnumerable <SqlExpression>?modifiers = null) { var result = factory.DateFunction("strftime", GetArguments(timestring, modifiers).Insert(0, factory.Constant(format)), timestring.Type); if (timestring.Type != returnType) { result = factory.Convert(result, returnType); } return(result); }
private static SqlExpression GetAndoyerDistance( SqlExpression left, SqlExpression right, Type resultType, ISqlExpressionFactory sqlExpressionFactory) { SqlExpression toDegrees(SqlExpression coord) => sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( coord, sqlExpressionFactory.Function( "PI", Array.Empty <SqlExpression>(), resultType)), sqlExpressionFactory.Constant(180.0)); SqlExpression xCoord(SqlExpression point) => sqlExpressionFactory.Function( "ST_X", new[] { point }, resultType); SqlExpression yCoord(SqlExpression point) => sqlExpressionFactory.Function( "ST_Y", new[] { point }, resultType); var c0 = sqlExpressionFactory.Constant(0.0); var c1 = sqlExpressionFactory.Constant(1.0); var c2 = sqlExpressionFactory.Constant(2.0); var c3 = sqlExpressionFactory.Constant(3.0); var c2Int = sqlExpressionFactory.Constant(2); var lon1 = toDegrees(xCoord(left)); var lat1 = toDegrees(yCoord(left)); var lon2 = toDegrees(xCoord(right)); var lat2 = toDegrees(yCoord(right)); var g = sqlExpressionFactory.Divide( sqlExpressionFactory.Subtract( lat1, lat2), c2); var lambda = sqlExpressionFactory.Divide( sqlExpressionFactory.Subtract( lon1, lon2), c2); var f = sqlExpressionFactory.Divide( sqlExpressionFactory.Add( lat1, lat2), c2); var sinG2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "SIN", new[] { g }, resultType), c2Int }, resultType); var cosG2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "COS", new[] { g }, resultType), c2Int }, resultType); var sinF2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "SIN", new[] { f }, resultType), c2Int }, resultType); var cosF2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "COS", new[] { f }, resultType), c2Int }, resultType); var sinL2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "SIN", new[] { lambda }, resultType), c2Int }, resultType); var cosL2 = sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "COS", new[] { lambda }, resultType), c2Int }, resultType); var s = sqlExpressionFactory.Add( sqlExpressionFactory.Multiply(sinG2, cosL2), sqlExpressionFactory.Multiply(cosF2, sinL2)); var c = sqlExpressionFactory.Add( sqlExpressionFactory.Multiply(cosG2, cosL2), sqlExpressionFactory.Multiply(sinF2, sinL2)); var radiusA = sqlExpressionFactory.Constant(6378137.0); var radiusB = sqlExpressionFactory.Constant(6356752.3142451793); var flattening = sqlExpressionFactory.Divide( sqlExpressionFactory.Subtract(radiusA, radiusB), radiusA); var omega = sqlExpressionFactory.Function( "ATAN", new[] { sqlExpressionFactory.Function( "SQRT", new[] { sqlExpressionFactory.Divide(s, c) }, resultType) }, resultType); var r3 = sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( c3, sqlExpressionFactory.Function( "SQRT", new[] { sqlExpressionFactory.Multiply(s, c) }, resultType)), omega); var d = sqlExpressionFactory.Multiply( sqlExpressionFactory.Multiply(c2, omega), radiusA); var h1 = sqlExpressionFactory.Divide( sqlExpressionFactory.Subtract(r3, c1), sqlExpressionFactory.Multiply(c2, c)); var h2 = sqlExpressionFactory.Divide( sqlExpressionFactory.Add(r3, c1), sqlExpressionFactory.Multiply(c2, s)); var andoyer = sqlExpressionFactory.Multiply( d, sqlExpressionFactory.Add( c1, sqlExpressionFactory.Multiply( flattening, sqlExpressionFactory.Subtract( sqlExpressionFactory.Multiply( sqlExpressionFactory.Multiply( h1, sinF2), cosG2), sqlExpressionFactory.Multiply( sqlExpressionFactory.Multiply( h2, cosF2), sinG2))))); return(sqlExpressionFactory.Case( new[] { new CaseWhenClause( sqlExpressionFactory.OrElse( sqlExpressionFactory.OrElse( sqlExpressionFactory.AndAlso( sqlExpressionFactory.Equal(lambda, c0), sqlExpressionFactory.Equal(g, c0)), sqlExpressionFactory.Equal(s, c0)), sqlExpressionFactory.Equal(c, c0)), c0), }, andoyer)); }
private static SqlExpression GetHaversineDistance( SqlExpression left, SqlExpression right, Type resultType, ISqlExpressionFactory sqlExpressionFactory) { // HAVERSINE = 6371000 * 2 * ASIN( // SQRT( // POWER(SIN((ST_Y(pt2) - ST_Y(pt1)) * pi()/180 / 2), 2) + // COS(ST_Y(pt1) * pi()/180) * // COS(ST_Y(pt2) * pi()/180) * // POWER(SIN((ST_X(pt2) - ST_X(pt1)) * pi()/180 / 2), 2) // ) // ) return(sqlExpressionFactory.Multiply( sqlExpressionFactory.Constant(6370986.0), // see https://postgis.net/docs/manual-1.4/ST_Distance_Sphere.html sqlExpressionFactory.Multiply( sqlExpressionFactory.Constant(2.0), sqlExpressionFactory.Function( "ASIN", new[] { sqlExpressionFactory.Function( "SQRT", new[] { sqlExpressionFactory.Add( sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "SIN", new[] { sqlExpressionFactory.Divide( sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( sqlExpressionFactory.Subtract( sqlExpressionFactory.Function( "ST_Y", new[] { right }, resultType), sqlExpressionFactory.Function( "ST_Y", new[] { left }, resultType)), sqlExpressionFactory.Function( "PI", Array.Empty <SqlExpression>(), resultType)), sqlExpressionFactory.Constant(180.0)), sqlExpressionFactory.Constant(2.0)) }, resultType), sqlExpressionFactory.Constant(2), }, resultType), sqlExpressionFactory.Multiply( sqlExpressionFactory.Function( "COS", new[] { sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( sqlExpressionFactory.Function( "ST_Y", new[] { left }, resultType), sqlExpressionFactory.Function( "PI", Array.Empty <SqlExpression>(), resultType)), sqlExpressionFactory.Constant(180.0)), }, resultType), sqlExpressionFactory.Multiply( sqlExpressionFactory.Function( "COS", new[] { sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( sqlExpressionFactory.Function( "ST_Y", new[] { right }, resultType), sqlExpressionFactory.Function( "PI", Array.Empty <SqlExpression>(), resultType)), sqlExpressionFactory.Constant(180.0)), }, resultType), sqlExpressionFactory.Function( "POWER", new SqlExpression[] { sqlExpressionFactory.Function( "SIN", new[] { sqlExpressionFactory.Divide( sqlExpressionFactory.Divide( sqlExpressionFactory.Multiply( sqlExpressionFactory.Subtract( sqlExpressionFactory.Function( "ST_X", new[] { right }, resultType), sqlExpressionFactory.Function( "ST_X", new[] { left }, resultType)), sqlExpressionFactory.Function( "PI", Array.Empty <SqlExpression>(), resultType)), sqlExpressionFactory.Constant(180.0)), sqlExpressionFactory.Constant(2.0)) }, resultType), sqlExpressionFactory.Constant(2), }, resultType)))) }, resultType) }, resultType)))); }
public static SqlExpression GetStDistanceSphereFunctionCall( SqlExpression left, SqlExpression right, SpatialDistanceAlgorithm algorithm, Type resultType, RelationalTypeMapping resultTypeMapping, ISqlExpressionFactory sqlExpressionFactory, IMySqlOptions options) { if (options.ServerVersion.SupportsSpatialDistanceSphereFunction) { if (algorithm == SpatialDistanceAlgorithm.Native) { return(sqlExpressionFactory.Function( "ST_Distance_Sphere", new[] { left, right }, resultType, resultTypeMapping)); } if (algorithm == SpatialDistanceAlgorithm.Andoyer && options.ServerVersion.SupportsSpatialDistanceFunctionImplementsAndoyer) { // The `ST_Distance()` in MySQL already uses the Andoyer algorithm, when SRID 4326 is associated // with the geometry. // CHECK: It might be faster to just run the custom implementation, if `ST_SRID()` does not support // a second parameter yet (see SetSrid()). return(sqlExpressionFactory.Case( new[] { new CaseWhenClause( sqlExpressionFactory.Equal( sqlExpressionFactory.Function( "ST_SRID", new[] { left }, typeof(int)), sqlExpressionFactory.Constant(4326)), GetStDistanceFunctionCall( left, right, resultType, resultTypeMapping, sqlExpressionFactory)) }, GetStDistanceFunctionCall( SetSrid(left, 4326, sqlExpressionFactory, options), SetSrid(right, 4326, sqlExpressionFactory, options), resultType, resultTypeMapping, sqlExpressionFactory))); } if (algorithm == SpatialDistanceAlgorithm.Haversine) { // The Haversine algorithm assumes planar coordinates. return(sqlExpressionFactory.Case( new[] { new CaseWhenClause( sqlExpressionFactory.Equal( sqlExpressionFactory.Function( "ST_SRID", new[] { left }, typeof(int)), sqlExpressionFactory.Constant(0)), GetHaversineDistance( left, right, resultType, sqlExpressionFactory)) }, GetHaversineDistance( SetSrid(left, 0, sqlExpressionFactory, options), SetSrid(right, 0, sqlExpressionFactory, options), resultType, sqlExpressionFactory))); } } if (algorithm == SpatialDistanceAlgorithm.Haversine) { return(GetHaversineDistance(left, right, resultType, sqlExpressionFactory)); } return(GetAndoyerDistance(left, right, resultType, sqlExpressionFactory)); }
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, MemberInfo member, Type 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); } 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); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (typeof(Polygon).IsAssignableFrom(member.DeclaringType)) { Check.DebugAssert(instance.TypeMapping != null, "Instance must have typeMapping assigned."); var storeType = instance.TypeMapping.StoreType; var isGeography = string.Equals(storeType, "geography", StringComparison.OrdinalIgnoreCase); if (isGeography) { if (Equals(_exteriorRing, member)) { return(_sqlExpressionFactory.Function( instance, "RingN", new[] { _sqlExpressionFactory.Constant(1) }, nullable: true, instancePropagatesNullability: true, argumentsPropagateNullability: new[] { false }, returnType, _typeMappingSource.FindMapping(returnType, storeType))); } if (Equals(_numInteriorRings, member)) { return(_sqlExpressionFactory.Subtract( _sqlExpressionFactory.Function( instance, "NumRings", Array.Empty <SqlExpression>(), nullable: true, instancePropagatesNullability: true, argumentsPropagateNullability: Array.Empty <bool>(), returnType), _sqlExpressionFactory.Constant(1))); } } if (_geometryMemberToFunctionName.TryGetValue(member, out var functionName)) { var resultTypeMapping = typeof(Geometry).IsAssignableFrom(returnType) ? _typeMappingSource.FindMapping(returnType, storeType) : _typeMappingSource.FindMapping(returnType); return(_sqlExpressionFactory.Function( instance, functionName, Array.Empty <SqlExpression>(), nullable: true, instancePropagatesNullability: true, argumentsPropagateNullability: Array.Empty <bool>(), returnType, resultTypeMapping)); } } return(null); }
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)); }
/// <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) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); Check.NotNull(logger, nameof(logger)); if (_indexOfMethodInfo.Equals(method)) { var argument = arguments[0]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, argument); return(_sqlExpressionFactory.Subtract( _sqlExpressionFactory.Function( "instr", new[] { _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(argument, stringTypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true, true }, method.ReturnType), _sqlExpressionFactory.Constant(1))); } if (_replaceMethodInfo.Equals(method)) { var firstArgument = arguments[0]; var secondArgument = arguments[1]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, firstArgument, secondArgument); return(_sqlExpressionFactory.Function( "replace", new[] { _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(firstArgument, stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(secondArgument, stringTypeMapping) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType, stringTypeMapping)); } if (_toLowerMethodInfo.Equals(method) || _toUpperMethodInfo.Equals(method)) { return(_sqlExpressionFactory.Function( _toLowerMethodInfo.Equals(method) ? "lower" : "upper", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, method.ReturnType, instance.TypeMapping)); } if (_substringMethodInfo.Equals(method)) { return(_sqlExpressionFactory.Function( "substr", new[] { instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType, instance.TypeMapping)); } if (_isNullOrWhiteSpaceMethodInfo.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(argument), _sqlExpressionFactory.Equal( _sqlExpressionFactory.Function( "trim", new[] { argument }, nullable: true, argumentsPropagateNullability: new[] { true }, argument.Type, argument.TypeMapping), _sqlExpressionFactory.Constant(string.Empty)))); } if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true || _trimStartMethodInfoWithCharArg?.Equals(method) == true || _trimStartMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments, "ltrim")); } if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true || _trimEndMethodInfoWithCharArg?.Equals(method) == true || _trimEndMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments, "rtrim")); } if (_trimMethodInfoWithoutArgs?.Equals(method) == true || _trimMethodInfoWithCharArg?.Equals(method) == true || _trimMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments, "trim")); } if (_containsMethodInfo.Equals(method)) { var pattern = arguments[0]; var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, pattern); instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping); pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping); return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.Equal( pattern, _sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)), _sqlExpressionFactory.GreaterThan( _sqlExpressionFactory.Function( "instr", new[] { instance, pattern }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(int)), _sqlExpressionFactory.Constant(0)))); } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.Function( "substr", new[] { argument, _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType)); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.Function( "substr", new[] { argument, _sqlExpressionFactory.Function( "length", new[] { argument }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(int)), _sqlExpressionFactory.Constant(1) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType)); } if (_startsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], true)); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], false)); } 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 (IndexOfMethodInfo.Equals(method)) { return(TranslateSystemFunction("INDEX_OF", typeof(int), instance, arguments[0])); } if (IndexOfMethodInfoWithStartingPosition.Equals(method)) { return(TranslateSystemFunction("INDEX_OF", typeof(int), instance, arguments[0], arguments[1])); } if (ReplaceMethodInfo.Equals(method)) { return(TranslateSystemFunction("REPLACE", method.ReturnType, instance, arguments[0], arguments[1])); } if (ContainsMethodInfo.Equals(method)) { return(TranslateSystemFunction("CONTAINS", typeof(bool), instance, arguments[0])); } if (StartsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("STARTSWITH", typeof(bool), instance, arguments[0])); } if (EndsWithMethodInfo.Equals(method)) { return(TranslateSystemFunction("ENDSWITH", typeof(bool), instance, arguments[0])); } if (ToLowerMethodInfo.Equals(method)) { return(TranslateSystemFunction("LOWER", method.ReturnType, instance)); } if (ToUpperMethodInfo.Equals(method)) { return(TranslateSystemFunction("UPPER", method.ReturnType, instance)); } if (TrimStartMethodInfoWithoutArgs?.Equals(method) == true || (TrimStartMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB LTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("LTRIM", method.ReturnType, instance)); } if (TrimEndMethodInfoWithoutArgs?.Equals(method) == true || (TrimEndMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB RTRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("RTRIM", method.ReturnType, instance)); } if (TrimMethodInfoWithoutArgs?.Equals(method) == true || (TrimMethodInfoWithCharArrayArg.Equals(method) // Cosmos DB TRIM does not take arguments && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) { return(TranslateSystemFunction("TRIM", method.ReturnType, instance)); } if (SubstringMethodInfoWithOneArg.Equals(method)) { return(TranslateSystemFunction( "SUBSTRING", method.ReturnType, instance, arguments[0], TranslateSystemFunction("LENGTH", typeof(int), instance))); } if (SubstringMethodInfoWithTwoArgs.Equals(method)) { return(arguments[0] is SqlConstantExpression constant && constant.Value is int intValue && intValue == 0 ? TranslateSystemFunction("LEFT", method.ReturnType, instance, arguments[1]) : TranslateSystemFunction("SUBSTRING", method.ReturnType, instance, arguments[0], arguments[1])); } } if (FirstOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("LEFT", typeof(char), arguments[0], _sqlExpressionFactory.Constant(1))); } if (LastOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(TranslateSystemFunction("RIGHT", typeof(char), arguments[0], _sqlExpressionFactory.Constant(1))); } if (StringConcatWithTwoArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], arguments[1])); } if (StringConcatWithThreeArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], arguments[2]))); } if (StringConcatWithFourArguments.Equals(method)) { return(_sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Add( arguments[1], _sqlExpressionFactory.Add( arguments[2], arguments[3])))); } if (StringComparisonWithComparisonTypeArgumentInstance.Equals(method) || StringComparisonWithComparisonTypeArgumentStatic.Equals(method)) { var comparisonTypeArgument = arguments[^ 1];