Exemple #1
0
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));

            if (Equals(method, _item))
            {
                return(_sqlExpressionFactory.Function(
                           "GeometryN",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1))
                },
                           method.ReturnType));
            }

            return(null);
        }
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList <SqlExpression> arguments)
        {
            if (typeof(ILineString).IsAssignableFrom(method.DeclaringType))
            {
                if (Equals(method.OnInterface(typeof(ILineString)), _getPointN))
                {
                    return(_sqlExpressionFactory.Function(
                               instance,
                               "STPointN",
                               new[] {
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            _sqlExpressionFactory.Constant(1))
                    },
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType, instance.TypeMapping.StoreType)));
                }
            }

            return(null);
        }
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));

            if (Equals(method, _item))
            {
                return(_sqlExpressionFactory.Function(
                           instance,
                           "STGeometryN",
                           new[]
                {
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1))
                },
                           method.ReturnType,
                           _typeMappingSource.FindMapping(typeof(Geometry), instance.TypeMapping.StoreType)));
            }

            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 (Equals(method, _getInteriorRingN))
            {
                return(_sqlExpressionFactory.Function(
                           "InteriorRingN",
                           new[] { instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1)) },
                           nullable: true,
                           argumentsPropagateNullability: new[] { true, true },
                           method.ReturnType));
            }

            return(null);
        }
Exemple #5
0
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (Equals(method, _isNullOrEmptyMethodInfo))
            {
                var argument = arguments[0];

                return(_sqlExpressionFactory.OrElse(
                           _sqlExpressionFactory.IsNull(argument),
                           _sqlExpressionFactory.Equal(
                               argument,
                               _sqlExpressionFactory.Constant(string.Empty))));
            }

            if (Equals(method, _concatMethodInfo))
            {
                return(_sqlExpressionFactory.Add(
                           arguments[0],
                           arguments[1]));
            }

            return(null);
        }
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual SqlExpression?Translate(
            SqlExpression?instance,
            MethodInfo method,
            IReadOnlyList <SqlExpression> arguments,
            IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (Equals(method, _isNullOrEmptyMethodInfo))
            {
                var argument = arguments[0];

                return(_sqlExpressionFactory.OrElse(
                           _sqlExpressionFactory.IsNull(argument),
                           _sqlExpressionFactory.Equal(
                               argument,
                               _sqlExpressionFactory.Constant(string.Empty))));
            }

            if (Equals(method, _concatMethodInfoTwoArgs))
            {
                return(_sqlExpressionFactory.Add(
                           arguments[0],
                           arguments[1]));
            }

            if (Equals(method, _concatMethodInfoThreeArgs))
            {
                return(_sqlExpressionFactory.Add(
                           arguments[0],
                           _sqlExpressionFactory.Add(
                               arguments[1],
                               arguments[2])));
            }

            if (Equals(method, _concatMethodInfoFourArgs))
            {
                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));

            if (Equals(method, _getPointN))
            {
                return(_sqlExpressionFactory.Function(
                           "PointN",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1))
                },
                           nullable: true,
                           argumentsPropagateNullability: new[] { true, true },
                           method.ReturnType));
            }

            return(null);
        }
Exemple #8
0
        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 },
                    nullable: true,
                    argumentsPropagateNullability: TrueArrays[1],
                    typeof(int))
            },
                nullable: true,
                argumentsPropagateNullability: TrueArrays[2],
                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.Constant(string.Empty)),
                       _sqlExpressionFactory.Equal(leftRight, castPattern))
                : _sqlExpressionFactory.Equal(leftRight, castPattern));
        }
        /// <summary>
        /// 更新 Select 语句
        /// </summary>
        /// <param name="selectExpression"></param>
        /// <returns></returns>
        private Expression VisitSelect(SelectExpression selectExpression)
        {
            var oldOffset = selectExpression.Offset;

            if (oldOffset == null)
            {
                return(selectExpression);
            }

            var oldLimit     = selectExpression.Limit;
            var oldOrderings = selectExpression.Orderings;

            // 在子查询中 OrderBy 必须写 Top 数量
            var newOrderings = oldOrderings.Count > 0 && (oldLimit != null || selectExpression == root)
                ? oldOrderings.ToList()
                : new List <OrderingExpression>();

            // 更新表达式
            selectExpression = selectExpression.Update(selectExpression.Projection.ToList(),
                                                       selectExpression.Tables.ToList(),
                                                       selectExpression.Predicate,
                                                       selectExpression.GroupBy.ToList(),
                                                       selectExpression.Having,
                                                       orderings: newOrderings,
                                                       limit: null,
                                                       offset: null);
            var rowOrderings = oldOrderings.Count != 0 ? oldOrderings
                : new[] { new OrderingExpression(new SqlFragmentExpression("(SELECT 1)"), true) };

            _ = selectExpression.PushdownIntoSubquery();    // .NET 6 该方法已无返回值

            var subQuery   = (SelectExpression)selectExpression.Tables[0];
            var projection = new RowNumberExpression(Array.Empty <SqlExpression>(), rowOrderings, oldOffset.TypeMapping);
            var left       = GenerateOuterColumnAccessor(subQuery, projection, "row");

            selectExpression.ApplyPredicate(sqlExpressionFactory.GreaterThan(left, oldOffset));

            if (oldLimit != null)
            {
                if (oldOrderings.Count == 0)
                {
                    selectExpression.ApplyPredicate(sqlExpressionFactory.LessThanOrEqual(left, sqlExpressionFactory.Add(oldOffset, oldLimit)));
                }
                else
                {
                    // 这里不支持子查询的 OrderBy 操作
                    selectExpression.ApplyLimit(oldLimit);
                }
            }
            return(selectExpression);
        }
Exemple #10
0
        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));
        }
        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);

                    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 (_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 (_startsWithMethodInfo.Equals(method))
                {
                    return(TranslateStartsEndsWith(instance, arguments[0], true));
                }

                if (_endsWithMethodInfo.Equals(method))
                {
                    return(TranslateStartsEndsWith(instance, arguments[0], false));
                }
            }

            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 (_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));
            }

            return(null);
        }
        public SqlExpression?Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (_toLowerMethodInfo.Equals(method) ||
                _toUpperMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER",
                           new[] { instance },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_substringMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           "SUBSTRING",
                           new[] { instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] },
                           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 }, argument.Type, argument.TypeMapping),
                               _sqlExpressionFactory.Constant(string.Empty))));
            }

            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 constantExpression)
                {
                    if (constantExpression.Value is string constantString)
                    {
                        return(constantString.Any(c => IsLikeWildChar(c))
                            ? _sqlExpressionFactory.Like(
                                   instance,
                                   _sqlExpressionFactory.Constant('%' + EscapeLikePattern(constantString) + '%'))
                            : _sqlExpressionFactory.Like(
                                   instance,
                                   _sqlExpressionFactory.Constant('%' + constantString + '%')));
                    }
                }
            }

            if (_startsWithMethodInfo.Equals(method))
            {
                return(TranslateStartsEndsWith(instance, arguments[0], true));
            }

            if (_endsWithMethodInfo.Equals(method))
            {
                return(TranslateStartsEndsWith(instance, arguments[0], false));
            }

            return(null);
        }
                private Expression VisitSelect(SelectExpression selectExpression)
                {
                    var oldOffset = selectExpression.Offset;

                    if (oldOffset == null)
                    {
                        return(selectExpression);
                    }

                    var oldLimit     = selectExpression.Limit;
                    var oldOrderings = selectExpression.Orderings;
                    //order by in subQuery without TOP N is invalid.
                    var newOrderings = oldOrderings.Count > 0 && (oldLimit != null || selectExpression == root)
                        ? oldOrderings.ToList()
                        : new List <OrderingExpression>();

                    selectExpression = selectExpression.Update(selectExpression.Projection.ToList(),
                                                               selectExpression.Tables.ToList(),
                                                               selectExpression.Predicate,
                                                               selectExpression.GroupBy.ToList(),
                                                               selectExpression.Having,
                                                               orderings: newOrderings,
                                                               limit: null,
                                                               offset: null,
                                                               selectExpression.IsDistinct,
                                                               selectExpression.Alias);
                    var rowOrderings = oldOrderings.Count != 0 ? oldOrderings
                        : new[] { new OrderingExpression(new SqlFragmentExpression("(SELECT 1)"), true) };

                    _ = selectExpression.PushdownIntoSubquery();
                    var subQuery   = (SelectExpression)selectExpression.Tables[0];
                    var projection = new RowNumberExpression(Array.Empty <SqlExpression>(), rowOrderings, oldOffset.TypeMapping);
                    var left       = GenerateOuterColumnAccessor(subQuery, projection, "row");

                    selectExpression.ApplyPredicate(sqlExpressionFactory.GreaterThan(left, oldOffset));
                    if (oldLimit != null)
                    {
                        if (oldOrderings.Count == 0)
                        {
                            selectExpression.ApplyPredicate(sqlExpressionFactory.LessThanOrEqual(left, sqlExpressionFactory.Add(oldOffset, oldLimit)));
                        }
                        else
                        {
                            //the above one not working when used as subQuery with orderBy
                            selectExpression.ApplyLimit(oldLimit);
                        }
                    }
                    return(selectExpression);
                }
        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);
        }
Exemple #15
0
        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 (_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);
        }
Exemple #16
0
        /// <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 (_substringMethodInfoWithOneArg.Equals(method))
                {
                    return(TranslateSystemFunction(
                               "SUBSTRING",
                               method.ReturnType,
                               instance,
                               arguments[0],
                               TranslateSystemFunction("LENGTH", typeof(int), instance)));
                }

                if (_substringMethodInfoWithTwoArgs.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);
        }
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));

            if (_methodToFunctionName.TryGetValue(method, out var functionName))
            {
                var finalArguments = new[] { instance }.Concat(arguments);

                if (method.ReturnType == typeof(bool))
                {
                    var nullCheck = (SqlExpression)_sqlExpressionFactory.IsNotNull(instance);
                    foreach (var argument in arguments)
                    {
                        nullCheck = _sqlExpressionFactory.AndAlso(
                            nullCheck,
                            _sqlExpressionFactory.IsNotNull(argument));
                    }

                    return(_sqlExpressionFactory.Case(
                               new[]
                    {
                        new CaseWhenClause(
                            nullCheck,
                            _sqlExpressionFactory.Function(
                                functionName,
                                finalArguments,
                                nullResultAllowed: false,
                                finalArguments.Select(a => false),
                                method.ReturnType))
                    },
                               null));
                }

                return(_sqlExpressionFactory.Function(
                           functionName,
                           finalArguments,
                           nullResultAllowed: true,
                           finalArguments.Select(a => true),
                           method.ReturnType));
            }

            if (Equals(method, _getGeometryN))
            {
                return(_sqlExpressionFactory.Function(
                           "GeometryN",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1))
                },
                           nullResultAllowed: true,
                           argumentsPropagateNullability: new[] { true, true },
                           method.ReturnType));
            }

            if (Equals(method, _isWithinDistance))
            {
                return(_sqlExpressionFactory.LessThanOrEqual(
                           _sqlExpressionFactory.Function(
                               "Distance",
                               new[] { instance, arguments[0] },
                               nullResultAllowed: true,
                               argumentsPropagateNullability: new[] { true, true },
                               typeof(double)),
                           arguments[1]));
            }

            return(null);
        }
        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);
        }
        /// <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 (typeof(Geometry).IsAssignableFrom(method.DeclaringType) &&
                instance != null)
            {
                var geometryExpressions = new[] { instance }.Concat(
                    arguments.Where(e => typeof(Geometry).IsAssignableFrom(e.Type)));
                var typeMapping = ExpressionExtensions.InferTypeMapping(geometryExpressions.ToArray());

                Check.DebugAssert(typeMapping != null, "At least one argument must have typeMapping.");
                var storeType   = typeMapping.StoreType;
                var isGeography = string.Equals(storeType, "geography", StringComparison.OrdinalIgnoreCase);

                if (_methodToFunctionName.TryGetValue(method, out var functionName) ||
                    (!isGeography && _geometryMethodToFunctionName.TryGetValue(method, out functionName)))
                {
                    instance = _sqlExpressionFactory.ApplyTypeMapping(
                        instance, _typeMappingSource.FindMapping(instance.Type, storeType));

                    var typeMappedArguments = new List <SqlExpression>();
                    foreach (var argument in arguments)
                    {
                        typeMappedArguments.Add(
                            _sqlExpressionFactory.ApplyTypeMapping(
                                argument,
                                typeof(Geometry).IsAssignableFrom(argument.Type)
                                    ? _typeMappingSource.FindMapping(argument.Type, storeType)
                                    : _typeMappingSource.FindMapping(argument.Type)));
                    }

                    var resultTypeMapping = typeof(Geometry).IsAssignableFrom(method.ReturnType)
                        ? _typeMappingSource.FindMapping(method.ReturnType, storeType)
                        : _typeMappingSource.FindMapping(method.ReturnType);

                    var finalArguments = Simplify(typeMappedArguments, isGeography);

                    var argumentsPropagateNullability = functionName == "STBuffer"
                        ? new[] { false }
                        : functionName == "STRelate"
                            ? new[] { true, false }
                            : finalArguments.Select(a => true).ToArray();

                    return(_sqlExpressionFactory.Function(
                               instance,
                               functionName,
                               finalArguments,
                               nullable: true,
                               instancePropagatesNullability: true,
                               argumentsPropagateNullability,
                               method.ReturnType,
                               resultTypeMapping));
                }

                if (Equals(method, _getGeometryN))
                {
                    return(_sqlExpressionFactory.Function(
                               instance,
                               "STGeometryN",
                               new[]
                    {
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            _sqlExpressionFactory.Constant(1))
                    },
                               nullable: true,
                               instancePropagatesNullability: true,
                               argumentsPropagateNullability: new[] { false },
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType, storeType)));
                }

                if (Equals(method, _isWithinDistance))
                {
                    instance = _sqlExpressionFactory.ApplyTypeMapping(
                        instance, _typeMappingSource.FindMapping(instance.Type, storeType));

                    var typeMappedArguments = new List <SqlExpression>();
                    foreach (var argument in arguments)
                    {
                        typeMappedArguments.Add(
                            _sqlExpressionFactory.ApplyTypeMapping(
                                argument,
                                typeof(Geometry).IsAssignableFrom(argument.Type)
                                    ? _typeMappingSource.FindMapping(argument.Type, storeType)
                                    : _typeMappingSource.FindMapping(argument.Type)));
                    }

                    var finalArguments = Simplify(new[] { typeMappedArguments[0] }, isGeography);

                    return(_sqlExpressionFactory.LessThanOrEqual(
                               _sqlExpressionFactory.Function(
                                   instance,
                                   "STDistance",
                                   finalArguments,
                                   nullable: true,
                                   instancePropagatesNullability: true,
                                   argumentsPropagateNullability: finalArguments.Select(a => true),
                                   typeof(double)),
                               typeMappedArguments[1]));
                }
            }

            return(null);
        }
Exemple #20
0
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (s_containsMethodInfo.Equals(method))
            {
                var pos = TranslateOneArgFunction("STRPOS", instance, arguments[0], typeof(long));
                return(_sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.GreaterThan(pos, _sqlExpressionFactory.Constant(0L))));
            }
            if (s_startsWithMethodInfo.Equals(method))
            {
                return(TranslateOneArgFunction("STARTS_WITH", instance, arguments[0], typeof(bool)));
            }
            if (s_endsWithMethodInfo.Equals(method))
            {
                return(TranslateOneArgFunction("ENDS_WITH", instance, arguments[0], typeof(bool)));
            }
            if (s_indexOfMethodInfo.Equals(method))
            {
                var pos = TranslateOneArgFunction("STRPOS", instance, arguments[0], typeof(long));
                return(_sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Subtract(pos, _sqlExpressionFactory.Constant(1L))));
            }
            if (s_replaceMethodInfo.Equals(method))
            {
                return(TranslateTwoArgFunction("REPLACE", instance, arguments[0], arguments[1], typeof(string)));
            }
            if (s_toLowerMethodInfo.Equals(method))
            {
                return(TranslateNoArgFunction("LOWER", instance, typeof(string)));
            }
            if (s_toUpperMethodInfo.Equals(method))
            {
                return(TranslateNoArgFunction("UPPER", instance, typeof(string)));
            }
            if (s_substringMethodInfo.Equals(method))
            {
                return(TranslateOneArgFunction("SUBSTR", instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1L)), typeof(string)));
            }
            if (s_substringWithLengthMethodInfo.Equals(method))
            {
                return(TranslateTwoArgFunction("SUBSTR", instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1L)), arguments[1], typeof(string)));
            }
            if (s_trimStartMethodInfoWithoutArgs.Equals(method))
            {
                return(TranslateNoArgFunction("LTRIM", instance, typeof(string)));
            }
            if (s_trimStartMethodInfoWithCharArg.Equals(method))
            {
                return(TranslateOneArgFunction("LTRIM", instance, arguments[0], typeof(string)));
            }
            if (s_trimEndMethodInfoWithoutArgs.Equals(method))
            {
                return(TranslateNoArgFunction("RTRIM", instance, typeof(string)));
            }
            if (s_trimEndMethodInfoWithCharArg.Equals(method))
            {
                return(TranslateOneArgFunction("RTRIM", instance, arguments[0], typeof(string)));
            }
            if (s_trimMethodInfoWithoutArgs.Equals(method))
            {
                return(TranslateNoArgFunction("TRIM", instance, typeof(string)));
            }
            if (s_trimMethodInfoWithCharArg.Equals(method))
            {
                return(TranslateOneArgFunction("TRIM", instance, arguments[0], typeof(string)));
            }
            if (s_padLeftMethodInfo.Equals(method))
            {
                return(TranslateOneArgFunction("LPAD", instance, arguments[0], typeof(string)));
            }
            if (s_padLeftWithStringMethodInfo.Equals(method))
            {
                return(TranslateTwoArgFunction("LPAD", instance, arguments[0], arguments[1], typeof(string)));
            }
            if (s_padRightMethodInfo.Equals(method))
            {
                return(TranslateOneArgFunction("RPAD", instance, arguments[0], typeof(string)));
            }
            if (s_padRightWithStringMethodInfo.Equals(method))
            {
                return(TranslateTwoArgFunction("RPAD", instance, arguments[0], arguments[1], typeof(string)));
            }
            if (s_formatOneArgMethodInfo.Equals(method) || s_formatTwoArgMethodInfo.Equals(method) || s_formatThreeArgMethodInfo.Equals(method) || s_formatVarArgMethodInfo.Equals(method))
            {
                return(TranslateStaticFunction("FORMAT", arguments, typeof(string)));
            }
            if (s_joinMethodInfo.Equals(method))
            {
                return(TranslateTwoArgFunction("ARRAY_TO_STRING", arguments[1], arguments[0], _sqlExpressionFactory.Constant(""), typeof(string)));
            }

            return(null);
        }
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (typeof(Geometry).IsAssignableFrom(method.DeclaringType))
            {
                var geometryExpressions = new[] { instance }.Concat(
                    arguments.Where(e => typeof(Geometry).IsAssignableFrom(e.Type)));
                var typeMapping = ExpressionExtensions.InferTypeMapping(geometryExpressions.ToArray());

                Debug.Assert(typeMapping != null, "At least one argument must have typeMapping.");
                var storeType = typeMapping.StoreType;

                if (_methodToFunctionName.TryGetValue(method, out var functionName) ||
                    _geometryMethodToFunctionName.TryGetValue(method, out functionName))
                {
                    instance = _sqlExpressionFactory.ApplyTypeMapping(
                        instance,
                        _typeMappingSource.FindMapping(instance.Type, storeType));

                    var typeMappedArguments = new List <SqlExpression>
                    {
                        instance
                    };

                    foreach (var argument in arguments)
                    {
                        typeMappedArguments.Add(
                            _sqlExpressionFactory.ApplyTypeMapping(
                                argument,
                                typeof(Geometry).IsAssignableFrom(argument.Type)
                                    ? _typeMappingSource.FindMapping(argument.Type, storeType)
                                    : _typeMappingSource.FindMapping(argument.Type)));
                    }

                    var resultTypeMapping = typeof(Geometry).IsAssignableFrom(method.ReturnType)
                        ? _typeMappingSource.FindMapping(method.ReturnType, storeType)
                        : _typeMappingSource.FindMapping(method.ReturnType);

                    return(_sqlExpressionFactory.Function(
                               functionName,
                               typeMappedArguments,
                               method.ReturnType,
                               resultTypeMapping));
                }

                if (Equals(method, _getGeometryN))
                {
                    return(_sqlExpressionFactory.Function(
                               "ST_GeometryN",
                               new[]
                    {
                        instance,
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            _sqlExpressionFactory.Constant(1))
                    },
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType, storeType)));
                }

                if (Equals(method, _distance))
                {
                    return(GetDistanceCallBySrid(
                               instance,
                               arguments[0],
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType, storeType)));
                }

                if (Equals(method, _isWithinDistance))
                {
                    return(_sqlExpressionFactory.LessThanOrEqual(
                               GetDistanceCallBySrid(
                                   instance,
                                   arguments[0],
                                   _distance.ReturnType,
                                   _typeMappingSource.FindMapping(_distance.ReturnType)),
                               arguments[1]));
                }
            }

            return(null);
        }
Exemple #22
0
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (_indexOfMethodInfo.Equals(method))
            {
                var argument          = arguments[0];
                var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, argument);
                argument = _sqlExpressionFactory.ApplyTypeMapping(argument, stringTypeMapping);

                var charIndexExpression = _sqlExpressionFactory.Subtract(
                    _sqlExpressionFactory.Function(
                        "CHARINDEX",
                        new[] { argument, _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping) },
                        method.ReturnType),
                    _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 },
                           method.ReturnType,
                           stringTypeMapping));
            }

            if (_toLowerMethodInfo.Equals(method) ||
                _toUpperMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER",
                           new[] { instance },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_substringMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           "SUBSTRING",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1)),
                    arguments[1]
                },
                           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 },
                        argument.Type,
                        argument.TypeMapping)
                },
                                   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 },
                           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 },
                           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 },
                        instance.Type,
                        instance.TypeMapping)
                },
                           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 ((string)constantPattern.Value == string.Empty)
                    {
                        return(_sqlExpressionFactory.Constant(true));
                    }

                    return(_sqlExpressionFactory.GreaterThan(
                               _sqlExpressionFactory.Function(
                                   "CHARINDEX",
                                   new[] { pattern, instance },
                                   typeof(int)),
                               _sqlExpressionFactory.Constant(0)));
                }

                return(_sqlExpressionFactory.OrElse(
                           _sqlExpressionFactory.Equal(
                               pattern,
                               _sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)),
                           _sqlExpressionFactory.GreaterThan(
                               _sqlExpressionFactory.Function(
                                   "CHARINDEX",
                                   new[] { pattern, instance },
                                   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);
        }
Exemple #23
0
        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")
                    },
                        nullable: true,
                        argumentsPropagateNullability: new[] { true, false },
                        method.ReturnType),
                    _sqlExpressionFactory.Constant(".")
                },
                           nullable: 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,
        IDiagnosticsLogger <DbLoggerCategory.Query> logger)
    {
        if (instance != null)
        {
            if (MethodToFunctionName.TryGetValue(method, out var functionName))
            {
                var finalArguments = new[] { instance }.Concat(arguments);

                if (method.ReturnType == typeof(bool))
                {
                    var nullCheck = (SqlExpression)_sqlExpressionFactory.IsNotNull(instance);
                    foreach (var argument in arguments)
                    {
                        nullCheck = _sqlExpressionFactory.AndAlso(
                            nullCheck,
                            _sqlExpressionFactory.IsNotNull(argument));
                    }

                    return(_sqlExpressionFactory.Case(
                               new[]
                    {
                        new CaseWhenClause(
                            nullCheck,
                            _sqlExpressionFactory.Function(
                                functionName,
                                finalArguments,
                                nullable: false,
                                finalArguments.Select(a => false),
                                method.ReturnType))
                    },
                               null));
                }

                return(_sqlExpressionFactory.Function(
                           functionName,
                           finalArguments,
                           nullable: true,
                           finalArguments.Select(a => true),
                           method.ReturnType));
            }

            if (Equals(method, GetGeometryN))
            {
                return(_sqlExpressionFactory.Function(
                           "GeometryN",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1))
                },
                           nullable: true,
                           argumentsPropagateNullability: new[] { true, true },
                           method.ReturnType));
            }

            if (Equals(method, IsWithinDistance))
            {
                return(_sqlExpressionFactory.LessThanOrEqual(
                           _sqlExpressionFactory.Function(
                               "Distance",
                               new[] { instance, arguments[0] },
                               nullable: true,
                               argumentsPropagateNullability: new[] { true, true },
                               typeof(double)),
                           arguments[1]));
            }
        }

        return(null);
    }
Exemple #25
0
    /// <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(TranslateIndexOf(instance, method, arguments[0], null));
            }

            if (IndexOfMethodInfoWithStartingPosition.Equals(method))
            {
                return(TranslateIndexOf(instance, method, arguments[0], arguments[1]));
            }

            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 (SubstringMethodInfoWithOneArg.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           "SUBSTRING",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1)),
                    _sqlExpressionFactory.Function(
                        "LEN",
                        new[] { instance },
                        nullable: true,
                        argumentsPropagateNullability: new[] { true },
                        typeof(int))
                },
                           nullable: true,
                           argumentsPropagateNullability: new[] { true, true, true },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (SubstringMethodInfoWithTwoArgs.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)
        {
            if (typeof(Geometry).IsAssignableFrom(method.DeclaringType))
            {
                var geometryExpressions = new[] { instance }.Concat(
                    arguments.Where(e => typeof(Geometry).IsAssignableFrom(e.Type)));
                var typeMapping = ExpressionExtensions.InferTypeMapping(geometryExpressions.ToArray());

                Debug.Assert(typeMapping != null, "At least one argument must have typeMapping.");
                var storeType   = typeMapping.StoreType;
                var isGeography = string.Equals(storeType, "geography", StringComparison.OrdinalIgnoreCase);

                if (_methodToFunctionName.TryGetValue(method, out var functionName) ||
                    (!isGeography && _geometryMethodToFunctionName.TryGetValue(method, out functionName)))
                {
                    instance = _sqlExpressionFactory.ApplyTypeMapping(
                        instance, _typeMappingSource.FindMapping(instance.Type, storeType));

                    var typeMappedArguments = new List <SqlExpression>();
                    foreach (var argument in arguments)
                    {
                        typeMappedArguments.Add(
                            _sqlExpressionFactory.ApplyTypeMapping(
                                argument,
                                typeof(Geometry).IsAssignableFrom(argument.Type)
                                    ? _typeMappingSource.FindMapping(argument.Type, storeType)
                                    : _typeMappingSource.FindMapping(argument.Type)));
                    }

                    var resultTypeMapping = typeof(Geometry).IsAssignableFrom(method.ReturnType)
                        ? _typeMappingSource.FindMapping(method.ReturnType, storeType)
                        : _typeMappingSource.FindMapping(method.ReturnType);

                    return(_sqlExpressionFactory.Function(
                               instance,
                               functionName,
                               Simplify(typeMappedArguments, isGeography),
                               method.ReturnType,
                               resultTypeMapping));
                }

                if (Equals(method, _getGeometryN))
                {
                    return(_sqlExpressionFactory.Function(
                               instance,
                               "STGeometryN",
                               new[] {
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            _sqlExpressionFactory.Constant(1))
                    },
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType, storeType)));
                }

                if (Equals(method, _isWithinDistance))
                {
                    instance = _sqlExpressionFactory.ApplyTypeMapping(
                        instance, _typeMappingSource.FindMapping(instance.Type, storeType));

                    var typeMappedArguments = new List <SqlExpression>();
                    foreach (var argument in arguments)
                    {
                        typeMappedArguments.Add(
                            _sqlExpressionFactory.ApplyTypeMapping(
                                argument,
                                typeof(Geometry).IsAssignableFrom(argument.Type)
                                    ? _typeMappingSource.FindMapping(argument.Type, storeType)
                                    : _typeMappingSource.FindMapping(argument.Type)));
                    }

                    return(_sqlExpressionFactory.LessThanOrEqual(
                               _sqlExpressionFactory.Function(
                                   instance,
                                   "STDistance",
                                   Simplify(new[] { typeMappedArguments[0] }, isGeography),
                                   typeof(double)),
                               typeMappedArguments[1]));
                }
            }

            return(null);
        }
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            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)
                }, true, null,
                               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)
                }, true, null,
                           method.ReturnType,
                           stringTypeMapping));
            }

            if (_toLowerMethodInfo.Equals(method) ||
                _toUpperMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           _toLowerMethodInfo.Equals(method) ? "lower" : "upper",
                           new[] { instance }, true, null,
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_substringMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           "substr",
                           new[] { instance, _sqlExpressionFactory.Add(arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] }, true, null,
                           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 }, true, null, 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 }, true, null,
                                   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);
        }
    /// <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];
Exemple #29
0
        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))));
        }