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") }, method.ReturnType), _sqlExpressionFactory.Constant(".") }, method.ReturnType)); } return(null); }
public virtual SqlExpression TranslateAverage([NotNull] Expression expression) { Check.NotNull(expression, nameof(expression)); if (!(expression is SqlExpression sqlExpression)) { sqlExpression = Translate(expression); } if (sqlExpression == null) { throw new InvalidOperationException(CoreStrings.TranslationFailed(expression.Print())); } var inputType = sqlExpression.Type.UnwrapNullableType(); if (inputType == typeof(int) || inputType == typeof(long)) { sqlExpression = _sqlExpressionFactory.ApplyDefaultTypeMapping( _sqlExpressionFactory.Convert(sqlExpression, typeof(double))); } return(inputType == typeof(float) ? _sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "AVG", new[] { sqlExpression }, typeof(double)), sqlExpression.Type, sqlExpression.TypeMapping) : (SqlExpression)_sqlExpressionFactory.Function( "AVG", new[] { sqlExpression }, sqlExpression.Type, sqlExpression.TypeMapping)); }
public virtual SqlExpression TranslateAverage(Expression expression) { if (!(expression is SqlExpression sqlExpression)) { sqlExpression = Translate(expression); } var inputType = sqlExpression.Type.UnwrapNullableType(); if (inputType == typeof(int) || inputType == typeof(long)) { sqlExpression = _sqlExpressionFactory.ApplyDefaultTypeMapping( _sqlExpressionFactory.Convert(sqlExpression, typeof(double))); } return(inputType == typeof(float) ? _sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "AVG", new[] { sqlExpression }, typeof(double), null), sqlExpression.Type, sqlExpression.TypeMapping) : (SqlExpression)_sqlExpressionFactory.Function( "AVG", new[] { sqlExpression }, sqlExpression.Type, sqlExpression.TypeMapping)); }
SqlExpression TranslateStartsEndsWith(SqlExpression instance, SqlExpression pattern, bool startsWith) { var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, pattern); instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping); pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping); if (pattern is SqlConstantExpression constantExpression) { // The pattern is constant. Aside from null, we escape all special characters (%, _, \) // in C# and send a simple LIKE return(constantExpression.Value is string constantPattern ? _sqlExpressionFactory.Like( instance, _sqlExpressionFactory.Constant( startsWith ? EscapeLikePattern(constantPattern) + '%' : '%' + EscapeLikePattern(constantPattern))) : _sqlExpressionFactory.Like(instance, _sqlExpressionFactory.Constant(null, stringTypeMapping))); } // The pattern is non-constant, we use LEFT or RIGHT to extract substring and compare. // For StartsWith we also first run a LIKE to quickly filter out most non-matching results (sargable, but imprecise // because of wildchars). SqlExpression leftRight = _sqlExpressionFactory.Function( startsWith ? "LEFT" : "RIGHT", new[] { instance, _sqlExpressionFactory.Function("LENGTH", new[] { pattern }, typeof(int)) }, typeof(string), stringTypeMapping); // LEFT/RIGHT of a citext return a text, so for non-default text mappings we apply an explicit cast. if (instance.TypeMapping != _textTypeMapping) { leftRight = _sqlExpressionFactory.Convert(leftRight, typeof(string), instance.TypeMapping); } // Also add an explicit cast on the pattern; this is only required because of // The following is only needed because of https://github.com/aspnet/EntityFrameworkCore/issues/19120 var castPattern = pattern.TypeMapping == _textTypeMapping ? pattern : _sqlExpressionFactory.Convert(pattern, typeof(string), pattern.TypeMapping); return(startsWith ? _sqlExpressionFactory.AndAlso( _sqlExpressionFactory.Like( instance, _sqlExpressionFactory.Add( pattern, _sqlExpressionFactory.Constant("%"))), _sqlExpressionFactory.Equal(leftRight, castPattern)) : _sqlExpressionFactory.Equal(leftRight, castPattern)); }
/// <inheritdoc /> public virtual SqlExpression Translate( SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (!MethodInfoDatePartMapping.TryGetValue(method, out var datePart)) { return(null); } var interval = arguments[0]; if (instance is null || interval is null) { return(null); } // Note: ideally we'd simply generate a PostgreSQL interval expression, but the .NET mapping of that is TimeSpan, // which does not work for months, years, etc. So we generate special fragments instead. if (interval is SqlConstantExpression constantExpression) { // We generate constant intervals as INTERVAL '1 days' if (constantExpression.Type == typeof(double) && ((double)constantExpression.Value >= int.MaxValue || (double)constantExpression.Value <= int.MinValue)) { return(null); } interval = _sqlExpressionFactory.Fragment(FormattableString.Invariant($"INTERVAL '{constantExpression.Value} {datePart}'")); } else { // For non-constants, we can't parameterize INTERVAL '1 days'. Instead, we use CAST($1 || ' days' AS interval). // Note that a make_interval() function also exists, but accepts only int (for all fields except for // seconds), so we don't use it. // Note: we instantiate SqlBinaryExpression manually rather than via sqlExpressionFactory because // of the non-standard Add expression (concatenate int with text) interval = _sqlExpressionFactory.Convert( new SqlBinaryExpression( ExpressionType.Add, _sqlExpressionFactory.Convert(interval, typeof(string), _textMapping), _sqlExpressionFactory.Constant(' ' + datePart, _textMapping), typeof(string), _textMapping), typeof(TimeSpan), _intervalMapping); } return(_sqlExpressionFactory.Add(instance, interval, instance.TypeMapping)); }
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { if (method == IPAddressParse) { return(_sqlExpressionFactory.Convert(arguments[0], typeof(IPAddress), _sqlExpressionFactory.FindMapping(typeof(IPAddress)))); } if (method == PhysicalAddressParse) { return(_sqlExpressionFactory.Convert(arguments[0], typeof(PhysicalAddress), _sqlExpressionFactory.FindMapping(typeof(PhysicalAddress)))); } if (method.DeclaringType != typeof(NpgsqlNetworkExtensions)) { return(null); } return(method.Name switch { nameof(NpgsqlNetworkExtensions.LessThan) => _sqlExpressionFactory.LessThan(arguments[1], arguments[2]), nameof(NpgsqlNetworkExtensions.LessThanOrEqual) => _sqlExpressionFactory.LessThanOrEqual(arguments[1], arguments[2]), nameof(NpgsqlNetworkExtensions.GreaterThanOrEqual) => _sqlExpressionFactory.GreaterThanOrEqual(arguments[1], arguments[2]), nameof(NpgsqlNetworkExtensions.GreaterThan) => _sqlExpressionFactory.GreaterThan(arguments[1], arguments[2]), nameof(NpgsqlNetworkExtensions.ContainedBy) => BoolReturningOnTwoNetworkTypes("<<"), nameof(NpgsqlNetworkExtensions.ContainedByOrEqual) => BoolReturningOnTwoNetworkTypes("<<="), nameof(NpgsqlNetworkExtensions.Contains) => BoolReturningOnTwoNetworkTypes(">>"), nameof(NpgsqlNetworkExtensions.ContainsOrEqual) => BoolReturningOnTwoNetworkTypes(">>="), nameof(NpgsqlNetworkExtensions.ContainsOrContainedBy) => BoolReturningOnTwoNetworkTypes("&&"), // TODO: Hack, see #1118 nameof(NpgsqlNetworkExtensions.BitwiseNot) => new SqlUnaryExpression(ExpressionType.Negate, arguments[1], arguments[1].Type, arguments[1].TypeMapping), nameof(NpgsqlNetworkExtensions.BitwiseAnd) => _sqlExpressionFactory.And(arguments[1], arguments[2]), nameof(NpgsqlNetworkExtensions.BitwiseOr) => _sqlExpressionFactory.Or(arguments[1], arguments[2]), // Add/Subtract accept inet + int, so we can't use the default type mapping inference logic which assumes // same-typed operands nameof(NpgsqlNetworkExtensions.Add) => new SqlBinaryExpression( ExpressionType.Add, _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[1]), _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[2]), arguments[1].Type, arguments[1].TypeMapping), nameof(NpgsqlNetworkExtensions.Subtract) when arguments[2].Type == typeof(int)
private SqlExpression TranslateIndexOf( SqlExpression instance, MethodInfo method, SqlExpression searchExpression, SqlExpression?startIndex) { var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, searchExpression) !; searchExpression = _sqlExpressionFactory.ApplyTypeMapping(searchExpression, stringTypeMapping); instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping); var charIndexArguments = new List <SqlExpression> { searchExpression, instance }; if (startIndex is not null) { charIndexArguments.Add(_sqlExpressionFactory.Add(startIndex, _sqlExpressionFactory.Constant(1))); } var argumentsPropagateNullability = Enumerable.Repeat(true, charIndexArguments.Count); 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", charIndexArguments, nullable: true, argumentsPropagateNullability, typeof(long)); charIndexExpression = _sqlExpressionFactory.Convert(charIndexExpression, typeof(int)); } else { charIndexExpression = _sqlExpressionFactory.Function( "CHARINDEX", charIndexArguments, nullable: true, argumentsPropagateNullability, method.ReturnType); } charIndexExpression = _sqlExpressionFactory.Subtract(charIndexExpression, _sqlExpressionFactory.Constant(1)); return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.Equal( searchExpression, _sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)), _sqlExpressionFactory.Constant(0)) }, charIndexExpression)); }
/// <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 (_methodInfoDataLengthMapping.Contains(method)) { var argument = arguments[1]; if (argument.TypeMapping == null) { argument = _sqlExpressionFactory.ApplyDefaultTypeMapping(argument); } if (_longReturningTypes.Contains(argument.TypeMapping.StoreType)) { var result = _sqlExpressionFactory.Function( "DATALENGTH", arguments.Skip(1), nullable: true, argumentsPropagateNullability: new[] { true }, typeof(long)); return(_sqlExpressionFactory.Convert(result, method.ReturnType.UnwrapNullableType())); } return(_sqlExpressionFactory.Function( "DATALENGTH", arguments.Skip(1), nullable: true, argumentsPropagateNullability: new[] { true }, method.ReturnType.UnwrapNullableType())); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart)) { // DateAdd does not accept number argument outside of int range // AddYears/AddMonths take int argument so no need to check for range return(!datePart.Equals("year") && !datePart.Equals("month") && arguments[0] is SqlConstantExpression sqlConstant && ((double)sqlConstant.Value >= int.MaxValue || (double)sqlConstant.Value <= int.MinValue) ? null : _sqlExpressionFactory.Function( "DATEADD", new[] { _sqlExpressionFactory.Fragment(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance }, nullable: true, argumentsPropagateNullability: new[] { false, true, true }, instance.Type, instance.TypeMapping)); } 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.Name == nameof(string.Length) && instance?.Type == typeof(string)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "LEN", new[] { instance }, nullable: true, argumentsPropagateNullability: new[] { true }, typeof(long)), returnType)); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { Check.NotNull(method, nameof(method)); Check.NotNull(arguments, nameof(arguments)); if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart)) { return(!datePart.Equals("year") && !datePart.Equals("month") && arguments[0] is SqlConstantExpression sqlConstant && ((double)sqlConstant.Value >= int.MaxValue || (double)sqlConstant.Value <= int.MinValue) ? null : _sqlExpressionFactory.Function( "DATEADD", new[] { _sqlExpressionFactory.Fragment(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance }, instance.Type, instance.TypeMapping)); } return(null); }
protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); if (TranslationFailed(unaryExpression.Operand, operand)) { return(null); } var sqlOperand = (SqlExpression)operand; switch (unaryExpression.NodeType) { case ExpressionType.Not: return(_sqlExpressionFactory.Not(sqlOperand)); case ExpressionType.Negate: return(_sqlExpressionFactory.Negate(sqlOperand)); case ExpressionType.Convert: // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } sqlOperand = _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlOperand); return(_sqlExpressionFactory.Convert(sqlOperand, unaryExpression.Type)); } 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 (method.IsGenericMethod && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains) && arguments[0].Type == typeof(byte[])) { var source = arguments[0]; var sourceTypeMapping = source.TypeMapping; var value = arguments[1] is SqlConstantExpression constantValue ? (SqlExpression)_sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value }, sourceTypeMapping) : _sqlExpressionFactory.Convert(arguments[1], typeof(byte[]), sourceTypeMapping); return(_sqlExpressionFactory.GreaterThan( _sqlExpressionFactory.Function( "CHARINDEX", new[] { value, source }, nullable: true, argumentsPropagateNullability: new[] { true, true }, typeof(int)), _sqlExpressionFactory.Constant(0))); } return(null); }
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments, IDiagnosticsLogger<DbLoggerCategory.Query> logger) { return _supportedMethods.Contains(method) ? _sqlExpressionFactory.Convert( arguments[0], method.ReturnType) : null; }
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { return(_supportedMethods.Contains(method) ? _sqlExpressionFactory.Convert( arguments[0], method.ReturnType) : 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); }
protected override ShapedQueryExpression TranslateAverage(ShapedQueryExpression source, LambdaExpression selector, Type resultType) { var selectExpression = (SelectExpression)source.QueryExpression; if (selectExpression.Limit != null || selectExpression.Offset != null) { selectExpression.PushdownIntoSubQuery(); } if (selector != null) { source = TranslateSelect(source, selector); } var projection = (SqlExpression)selectExpression.GetProjectionExpression(new ProjectionMember()); var inputType = projection.Type.UnwrapNullableType(); if (inputType == typeof(int) || inputType == typeof(long)) { projection = _sqlExpressionFactory.ApplyDefaultTypeMapping( _sqlExpressionFactory.Convert(projection, typeof(double))); } if (inputType == typeof(float)) { projection = _sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "AVG", new[] { projection }, typeof(double), null), projection.Type, projection.TypeMapping); } else { projection = _sqlExpressionFactory.Function( "AVG", new[] { projection }, projection.Type, projection.TypeMapping); } return(AggregateResultShaper(source, projection, throwOnNullResult: true, resultType)); }
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { var password = _expressionFactory.Constant(Password); if (method == _nonTranslatableEncryptMethod || method == __nonTranslatableDecryptMethod) { throw new Exception("Dont use this this will not translate to SQL. Use Decrypt() or Encrypt()"); } if (method == _encryptStringMethod) { var value = arguments[0]; var aesToHexExpression = AESEncryptionBuild(instance, password, value); return(aesToHexExpression); } if (method == _encryptMethod) { var value = arguments[1]; var aesToHexExpression = AESEncryptionBuild(instance, password, value); return(aesToHexExpression); } if (method == _decryptMethod) { var value = arguments[1]; var aesDecryptExpression = AesDecryptExpression(instance, password, value); return(_expressionFactory.Convert(aesDecryptExpression, typeof(string))); } if (method == _decryptStringMethod) { var value = arguments[0]; var aesDecryptExpression = AesDecryptExpression(instance, password, value); return(_expressionFactory.Convert(aesDecryptExpression, typeof(string))); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { if (member.Name == nameof(string.Length) && instance?.Type == typeof(string)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.Function("LEN", new[] { instance }, typeof(long)), returnType)); } return(null); }
public virtual SqlExpression Translate( SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { // Translates parameterless Object.ToString() calls. return(method.Name == nameof(ToString) && arguments.Count == 0 && instance != null && _supportedTypes.Contains(instance.Type.UnwrapNullableType()) ? _sqlExpressionFactory.Convert(instance, typeof(string)) : 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) => _methodInfo.Equals(method) ? _sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "ISDATE", new[] { arguments[1] }, nullable: true, argumentsPropagateNullability: new[] { true }, _methodInfo.ReturnType), _methodInfo.ReturnType) : null;
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 (_methodInfoDatePartMapping.TryGetValue(method, out var datePart) && instance != null) { // DateAdd does not accept number argument outside of int range // AddYears/AddMonths take int argument so no need to check for range if (datePart != "year" && datePart != "month" && arguments[0] is SqlConstantExpression sqlConstant && sqlConstant.Value is double doubleValue && (doubleValue >= int.MaxValue || doubleValue <= int.MinValue)) { return(null); } if (instance is SqlConstantExpression instanceConstant) { instance = instanceConstant.ApplyTypeMapping(_typeMappingSource.FindMapping(typeof(DateTime), "datetime")); } return(_sqlExpressionFactory.Function( "DATEADD", new[] { _sqlExpressionFactory.Fragment(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance }, nullable: true, argumentsPropagateNullability: new[] { false, true, true }, instance.Type, instance.TypeMapping)); } return(null); }
public virtual SqlExpression Translate( SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (instance == null || method.Name != nameof(ToString) || arguments.Count != 0) { return(null); } if (instance.Type == typeof(bool)) { return(instance is ColumnExpression columnExpression && columnExpression.IsNullable ? _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)), _sqlExpressionFactory.Constant(false.ToString())), new CaseWhenClause( _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(true)), _sqlExpressionFactory.Constant(true.ToString())) }, _sqlExpressionFactory.Constant(null)) : _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)), _sqlExpressionFactory.Constant(false.ToString())) }, _sqlExpressionFactory.Constant(true.ToString()))); } // Translates parameterless Object.ToString() calls. return(_supportedTypes.Contains(instance.Type.UnwrapNullableType()) ? _sqlExpressionFactory.Convert(instance, typeof(string)) : null); }
//protected override Expression VisitNew(NewExpression newExpression) //{ // if (newExpression.Members == null // || newExpression.Arguments.Count == 0) // { // return null; // } // var bindings = new Expression[newExpression.Arguments.Count]; // for (var i = 0; i < bindings.Length; i++) // { // var translation = Visit(newExpression.Arguments[i]); // if (translation == null) // { // return null; // } // bindings[i] = translation; // } // return Expression.Constant(bindings); //} protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); if (TranslationFailed(unaryExpression.Operand, operand)) { return(null); } // In certain cases EF.Property would have convert node around the source. if (operand is EntityShaperExpression && unaryExpression.Type == typeof(object) && unaryExpression.NodeType == ExpressionType.Convert) { return(operand); } var sqlOperand = (SqlExpression)operand; if (unaryExpression.NodeType == ExpressionType.Convert) { // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } sqlOperand = _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlOperand); return(_sqlExpressionFactory.Convert(sqlOperand, unaryExpression.Type)); } if (unaryExpression.NodeType == ExpressionType.Not) { return(_sqlExpressionFactory.Not(sqlOperand)); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { if (method.IsGenericMethod && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains) && arguments[0].Type == typeof(byte[])) { var source = arguments[0]; var sourceTypeMapping = source.TypeMapping; var value = arguments[1] is SqlConstantExpression constantValue ? (SqlExpression)_sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value }, sourceTypeMapping) : _sqlExpressionFactory.Convert(arguments[1], typeof(byte[]), sourceTypeMapping); return(_sqlExpressionFactory.GreaterThan( _sqlExpressionFactory.Function("CHARINDEX", new[] { value, source }, typeof(int)), _sqlExpressionFactory.Constant(0))); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (member.Name == nameof(string.Length) && instance?.Type == typeof(string)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "LEN", new[] { instance }, nullResultAllowed: true, argumentsPropagateNullability: new[] { true }, typeof(long)), returnType)); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { member.ThrowIfNull(nameof(member)); returnType.ThrowIfNull(nameof(returnType)); logger.ThrowIfNull(nameof(logger)); if (member.Name == nameof(string.Length) && instance?.Type == typeof(string)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.Function( "LEN", new[] { instance }, true, new[] { true }, typeof(int)), returnType)); } return(null); }
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 (_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 (_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) { if (!(constantPattern.Value is string patternValue)) { return(_sqlExpressionFactory.Like( instance, _sqlExpressionFactory.Constant(null, stringTypeMapping))); } if (patternValue.Length == 0) { return(_sqlExpressionFactory.Constant(true)); } return(patternValue.Any(IsLikeWildChar) ? _sqlExpressionFactory.Like( instance, _sqlExpressionFactory.Constant($"%{EscapeLikePattern(patternValue)}%"), _sqlExpressionFactory.Constant(LikeEscapeString)) : _sqlExpressionFactory.Like(instance, _sqlExpressionFactory.Constant($"%{patternValue}%"))); } return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.Like( 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 (_startsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], true)); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], false)); } } if (_isNullOrEmptyMethodInfo.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(argument), _sqlExpressionFactory.Like( argument, _sqlExpressionFactory.Constant(string.Empty)))); } if (_isNullOrWhiteSpaceMethodInfo.Equals(method)) { var argument = arguments[0]; return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(argument), _sqlExpressionFactory.Equal( argument, _sqlExpressionFactory.Constant(string.Empty, argument.TypeMapping)))); } 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)); } return(null); }
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 (_startsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], true)); } if (_endsWithMethodInfo.Equals(method)) { return(TranslateStartsEndsWith(instance, arguments[0], false)); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { Check.NotNull(member, nameof(member)); Check.NotNull(returnType, nameof(returnType)); if (member.DeclaringType == typeof(DateTime)) { var memberName = member.Name; if (_datePartMapping.TryGetValue(memberName, out var datePart)) { return(_sqlExpressionFactory.Convert( SqliteExpression.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 }, nullable: true, argumentsPropagateNullability: new[] { true }, 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( SqliteExpression.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); } Check.DebugAssert(timestring != null, "timestring is null"); return(_sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { _sqlExpressionFactory.Function( "rtrim", new SqlExpression[] { SqliteExpression.Strftime( _sqlExpressionFactory, returnType, format, timestring, modifiers), _sqlExpressionFactory.Constant("0") }, nullable: true, argumentsPropagateNullability: new[] { true, false }, returnType), _sqlExpressionFactory.Constant(".") }, nullable: true, argumentsPropagateNullability: new[] { true, false }, returnType)); } return(null); }