/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { if (_likeMethodInfos.Any(m => Equals(method, m))) { var match = _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[1]); var pattern = InferStringTypeMappingOrApplyDefault( arguments[2], match.TypeMapping); var excapeChar = arguments.Count == 4 ? InferStringTypeMappingOrApplyDefault( arguments[3], match.TypeMapping) : null; return(_sqlExpressionFactory.Like( match, pattern, excapeChar)); } if (Equals(method, _matchMethodInfo)) { if (arguments[3] is SqlConstantExpression constant) { return(_sqlExpressionFactory.MakeMatch( arguments[1], arguments[2], (MySqlMatchSearchMode)constant.Value)); } if (arguments[3] is SqlParameterExpression parameter) { // Use nested OR clauses here, because MariaDB does not support MATCH...AGAINST from inside of // CASE statements and the nested OR clauses use the fulltext index, while using CASE does not: // <search_mode_1> = @p AND MATCH ... AGAINST ... OR // <search_mode_2> = @p AND MATCH ... AGAINST ... OR [...] var andClauses = Enum.GetValues(typeof(MySqlMatchSearchMode)) .Cast <MySqlMatchSearchMode>() .OrderByDescending(m => m) .Select(m => _sqlExpressionFactory.AndAlso( _sqlExpressionFactory.Equal(parameter, _sqlExpressionFactory.Constant(m)), _sqlExpressionFactory.MakeMatch(arguments[1], arguments[2], m))) .ToArray(); return(andClauses .Skip(1) .Aggregate( andClauses.First(), (currentAnd, previousExpression) => _sqlExpressionFactory.OrElse(previousExpression, currentAnd))); } } return(null); }
private SqlBinaryExpression MakeStartsWithExpressionImpl( SqlExpression target, SqlExpression prefix, SqlExpression originalPrefix = null) { // BUG: EF Core #17389 will lead to a System.NullReferenceException, if SqlExpressionFactory.Like() // is being called with match and pattern as two expressions, that have not been applied a // TypeMapping yet and no escapeChar (null). // As a workaround, apply/infer the type mapping for the match expression manually for now. return(_sqlExpressionFactory.AndAlso( _sqlExpressionFactory.Like( target, _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Function( "CONCAT", // when performing the like it is preferable to use the untransformed prefix // value to ensure the index can be used new[] { originalPrefix ?? prefix, _sqlExpressionFactory.Constant("%") }, typeof(string)))), _sqlExpressionFactory.Equal( _sqlExpressionFactory.Function( "LEFT", new[] { target, CharLength(prefix) }, typeof(string)), prefix ))); }
private SqlExpression ApplyPathLocationTypeMapping(SqlExpression expression) { var pathLocation = _sqlExpressionFactory.ApplyDefaultTypeMapping(expression); // Path locations are usually made of strings. And they should be rendered without surrounding quotes. if (pathLocation is SqlConstantExpression sqlConstantExpression && sqlConstantExpression.TypeMapping is MySqlStringTypeMapping stringTypeMapping && !stringTypeMapping.IsUnquoted) { pathLocation = sqlConstantExpression.ApplyTypeMapping(stringTypeMapping.Clone(true)); } return(pathLocation); }
public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType) { if (instance?.TypeMapping is MySqlJsonTypeMapping || instance is MySqlJsonTraversalExpression) { // Path locations need to be rendered without surrounding quotes, because the path itself already // has quotes. var sqlConstantExpression = _sqlExpressionFactory.ApplyDefaultTypeMapping( _sqlExpressionFactory.Constant( GetJsonPropertyName(member) ?? member.Name, _unquotedStringTypeMapping)); return(TranslateMemberAccess( instance, sqlConstantExpression, returnType)); } return(null); }
public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments) { var traversal = GetTraversalExpression(instance, arguments); if (traversal == null) { return(null); } if (typeof(JToken).IsAssignableFrom(method.DeclaringType) && method.Name == "get_Item" && arguments.Count == 1) { var indexExpression = _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0]); if (method.DeclaringType == typeof(JArray) || indexExpression.Type == typeof(int)) { // Try translating indexing inside json column. return(_jsonPocoTranslator.TranslateMemberAccess( traversal, _sqlExpressionFactory.JsonArrayIndex(indexExpression), method.ReturnType)); } return(traversal.Append( ApplyPathLocationTypeMapping(arguments[0]), method.DeclaringType, _typeMappingSource.FindMapping(method.DeclaringType))); } // Support for .Value<T>() and .Value<U, T>(): if (instance == null && method.Name == nameof(global::Newtonsoft.Json.Linq.Extensions.Value) && method.DeclaringType == typeof(global::Newtonsoft.Json.Linq.Extensions) && method.IsGenericMethod && method.GetParameters().Length == 1 && arguments.Count == 1) { return(ConvertFromJsonExtract( traversal.Clone( method.ReturnType == typeof(string), method.ReturnType, _typeMappingSource.FindMapping(method.ReturnType) ), method.ReturnType)); } // Support for Count() if (instance == null && method.Name == nameof(Enumerable.Count) && method.DeclaringType == typeof(Enumerable) && method.IsGenericMethod && method.GetParameters().Length == 1 && arguments.Count == 1) { return(_jsonPocoTranslator.TranslateArrayLength(traversal)); } // Predicate-less Any - translate to a simple length check. if (method.IsClosedFormOf(_enumerableAnyWithoutPredicate) && arguments.Count == 1 && arguments[0].Type.TryGetElementType(out _) && arguments[0].TypeMapping is MySqlJsonTypeMapping) { return(_sqlExpressionFactory.GreaterThan( _jsonPocoTranslator.TranslateArrayLength(arguments[0]), _sqlExpressionFactory.Constant(0))); } return(null); }