/// <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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }