public SqlExpression MakeStringEqualsExpression(
     [NotNull] SqlExpression leftValue,
     [NotNull] SqlExpression rightValue,
     [NotNull] SqlExpression stringComparison)
 {
     if (TryGetExpressionValue <StringComparison>(stringComparison, out var cmp))
     {
         return(CreateExpressionForCaseSensitivity(
                    cmp,
                    () =>
         {
             if (leftValue is ColumnExpression)
             {
                 // Applying the binary operator to the non-column value enables SQL to
                 // utilize an index if one exists.
                 return _sqlExpressionFactory.Equal(
                     leftValue,
                     Utf8Bin(rightValue)
                     );
             }
             else
             {
                 return _sqlExpressionFactory.Equal(
                     Utf8Bin(leftValue),
                     rightValue
                     );
             }
         },
                    () =>
                    _sqlExpressionFactory.Equal(
                        LCase(leftValue),
                        Utf8Bin(LCase(rightValue))
                        )
                    ));
     }
     else
     {
         return(new CaseExpression(
                    new[]
         {
             new CaseWhenClause(
                 _sqlExpressionFactory.In(stringComparison, _caseSensitiveComparisons, false),
                 // Case sensitive, accent sensitive
                 _sqlExpressionFactory.Equal(
                     leftValue,
                     Utf8Bin(rightValue)
                     )
                 )
         },
                    // Case insensitive, accent sensitive
                    _sqlExpressionFactory.Equal(
                        LCase(leftValue),
                        Utf8Bin(LCase(rightValue))
                        )
                    ));
     }
 }
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (_likeMethodInfos.Any(m => Equals(method, m)))
            {
                var match = _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[1]);

                var pattern = InferStringTypeMappingOrApplyDefault(
                    arguments[2],
                    match.TypeMapping);

                var excapeChar = arguments.Count == 4
                    ? InferStringTypeMappingOrApplyDefault(
                    arguments[3],
                    match.TypeMapping)
                    : null;

                return(_sqlExpressionFactory.Like(
                           match,
                           pattern,
                           excapeChar));
            }

            if (Equals(method, _matchMethodInfo))
            {
                if (arguments[3] is SqlConstantExpression constant)
                {
                    return(_sqlExpressionFactory.MakeMatch(
                               arguments[1],
                               arguments[2],
                               (MySqlMatchSearchMode)constant.Value));
                }

                if (arguments[3] is SqlParameterExpression parameter)
                {
                    // Use nested OR clauses here, because MariaDB does not support MATCH...AGAINST from inside of
                    // CASE statements and the nested OR clauses use the fulltext index, while using CASE does not:
                    // <search_mode_1> = @p AND MATCH ... AGAINST ... OR
                    // <search_mode_2> = @p AND MATCH ... AGAINST ... OR [...]
                    var andClauses = Enum.GetValues(typeof(MySqlMatchSearchMode))
                                     .Cast <MySqlMatchSearchMode>()
                                     .OrderByDescending(m => m)
                                     .Select(m => _sqlExpressionFactory.AndAlso(
                                                 _sqlExpressionFactory.Equal(parameter, _sqlExpressionFactory.Constant(m)),
                                                 _sqlExpressionFactory.MakeMatch(arguments[1], arguments[2], m)))
                                     .ToArray();

                    return(andClauses
                           .Skip(1)
                           .Aggregate(
                               andClauses.First(),
                               (currentAnd, previousExpression) => _sqlExpressionFactory.OrElse(previousExpression, currentAnd)));
                }
            }

            return(null);
        }
Пример #3
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (Equals(method, _spatialDistancePlanarMethodInfo))
            {
                // MySQL 8 uses the Andoyer algorithm by default for `ST_Distance()`, if an SRID of 4326 has been
                // associated with the geometry.
                // Since this call explicitly asked for a planar distance calculation, we need to ensure that
                // MySQL actually does that.
                // MariaDB ignores SRIDs and always calculates the planar distance.
                // CHECK: It could be faster to just manually apply the Pythagoras Theorem instead of changing the
                //        SRID in the case where ST_SRID() does not support a second parameter yet (see
                //        SetSrid()).
                if (_options.ServerVersion.Supports.SpatialSupportFunctionAdditions &&
                    _options.ServerVersion.Supports.SpatialDistanceFunctionImplementsAndoyer)
                {
                    return(_sqlExpressionFactory.Case(
                               new[]
                    {
                        new CaseWhenClause(
                            _sqlExpressionFactory.Equal(
                                _sqlExpressionFactory.NullableFunction(
                                    "ST_SRID",
                                    new[] { arguments[1] },
                                    typeof(int)),
                                _sqlExpressionFactory.Constant(0)),
                            GetStDistanceFunctionCall(
                                arguments[1],
                                arguments[2],
                                method.ReturnType,
                                _typeMappingSource.FindMapping(method.ReturnType),
                                _sqlExpressionFactory))
                    },
                               GetStDistanceFunctionCall(
                                   SetSrid(arguments[1], 0, _sqlExpressionFactory, _options),
                                   SetSrid(arguments[2], 0, _sqlExpressionFactory, _options),
                                   method.ReturnType,
                                   _typeMappingSource.FindMapping(method.ReturnType),
                                   _sqlExpressionFactory)));
                }

                return(GetStDistanceFunctionCall(
                           arguments[1],
                           arguments[2],
                           method.ReturnType,
                           _typeMappingSource.FindMapping(method.ReturnType),
                           _sqlExpressionFactory));
            }

            if (Equals(method, _spatialDistanceSphere))
            {
                if (!(arguments[3] is SqlConstantExpression algorithm))
                {
                    throw new InvalidOperationException("The 'algorithm' parameter must be supplied as a constant.");
                }

                return(GetStDistanceSphereFunctionCall(
                           arguments[1],
                           arguments[2],
                           (SpatialDistanceAlgorithm)algorithm.Value,
                           method.ReturnType,
                           _typeMappingSource.FindMapping(method.ReturnType),
                           _sqlExpressionFactory,
                           _options));
            }

            return(null);
        }
Пример #4
0
        private static SqlExpression GetAndoyerDistance(
            SqlExpression left,
            SqlExpression right,
            Type resultType,
            MySqlSqlExpressionFactory sqlExpressionFactory)
        {
            SqlExpression toDegrees(SqlExpression coord)
            => sqlExpressionFactory.Divide(
                sqlExpressionFactory.Multiply(
                    coord,
                    sqlExpressionFactory.NonNullableFunction(
                        "PI",
                        Array.Empty <SqlExpression>(),
                        resultType)),
                sqlExpressionFactory.Constant(180.0));

            SqlExpression xCoord(SqlExpression point)
            => sqlExpressionFactory.NullableFunction(
                "ST_X",
                new[] { point },
                resultType);

            SqlExpression yCoord(SqlExpression point)
            => sqlExpressionFactory.NullableFunction(
                "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.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "SIN",
                    new[] { g },
                    resultType),
                c2Int
            },
                resultType);
            var cosG2 = sqlExpressionFactory.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "COS",
                    new[] { g },
                    resultType),
                c2Int
            },
                resultType);
            var sinF2 = sqlExpressionFactory.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "SIN",
                    new[] { f },
                    resultType),
                c2Int
            },
                resultType);
            var cosF2 = sqlExpressionFactory.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "COS",
                    new[] { f },
                    resultType),
                c2Int
            },
                resultType);
            var sinL2 = sqlExpressionFactory.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "SIN",
                    new[] { lambda },
                    resultType),
                c2Int
            },
                resultType);
            var cosL2 = sqlExpressionFactory.NullableFunction(
                "POWER",
                new SqlExpression[]
            {
                sqlExpressionFactory.NullableFunction(
                    "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.NullableFunction(
                "ATAN",
                new[]
            {
                sqlExpressionFactory.NullableFunction(
                    "SQRT",
                    new[] { sqlExpressionFactory.Divide(s, c) },
                    resultType,
                    false)
            },
                resultType);
            var r3 = sqlExpressionFactory.Divide(
                sqlExpressionFactory.Multiply(
                    c3,
                    sqlExpressionFactory.NullableFunction(
                        "SQRT",
                        new[] { sqlExpressionFactory.Multiply(s, c) },
                        resultType,
                        false)),
                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));
        }
Пример #5
0
        public static SqlExpression GetStDistanceSphereFunctionCall(
            SqlExpression left,
            SqlExpression right,
            SpatialDistanceAlgorithm algorithm,
            Type resultType,
            RelationalTypeMapping resultTypeMapping,
            MySqlSqlExpressionFactory sqlExpressionFactory,
            IMySqlOptions options)
        {
            if (options.ServerVersion.Supports.SpatialDistanceSphereFunction)
            {
                if (algorithm == SpatialDistanceAlgorithm.Native)
                {
                    // Returns null for empty geometry arguments.
                    return(sqlExpressionFactory.NullableFunction(
                               "ST_Distance_Sphere",
                               new[] { left, right },
                               resultType,
                               resultTypeMapping,
                               false));
                }

                if (algorithm == SpatialDistanceAlgorithm.Andoyer &&
                    options.ServerVersion.Supports.SpatialDistanceFunctionImplementsAndoyer)
                {
                    // The `ST_Distance()` in MySQL already uses the Andoyer algorithm, when SRID 4326 is associated
                    // with the geometry.
                    // CHECK: It might be faster to just run the custom implementation, if `ST_SRID()` does not support
                    //        a second parameter yet (see SetSrid()).
                    return(sqlExpressionFactory.Case(
                               new[]
                    {
                        new CaseWhenClause(
                            sqlExpressionFactory.Equal(
                                sqlExpressionFactory.NullableFunction(
                                    "ST_SRID",
                                    new[] { left },
                                    typeof(int)),
                                sqlExpressionFactory.Constant(4326)),
                            GetStDistanceFunctionCall(
                                left,
                                right,
                                resultType,
                                resultTypeMapping,
                                sqlExpressionFactory))
                    },
                               GetStDistanceFunctionCall(
                                   SetSrid(left, 4326, sqlExpressionFactory, options),
                                   SetSrid(right, 4326, sqlExpressionFactory, options),
                                   resultType,
                                   resultTypeMapping,
                                   sqlExpressionFactory)));
                }

                if (algorithm == SpatialDistanceAlgorithm.Haversine)
                {
                    // The Haversine algorithm assumes planar coordinates.
                    return(sqlExpressionFactory.Case(
                               new[]
                    {
                        new CaseWhenClause(
                            sqlExpressionFactory.Equal(
                                sqlExpressionFactory.NullableFunction(
                                    "ST_SRID",
                                    new[] { left },
                                    typeof(int)),
                                sqlExpressionFactory.Constant(0)),
                            GetHaversineDistance(
                                left,
                                right,
                                resultType,
                                sqlExpressionFactory))
                    },
                               GetHaversineDistance(
                                   SetSrid(left, 0, sqlExpressionFactory, options),
                                   SetSrid(right, 0, sqlExpressionFactory, options),
                                   resultType,
                                   sqlExpressionFactory)));
                }
            }

            if (algorithm == SpatialDistanceAlgorithm.Haversine)
            {
                return(GetHaversineDistance(left, right, resultType, sqlExpressionFactory));
            }

            return(GetAndoyerDistance(left, right, resultType, sqlExpressionFactory));
        }
 private SqlExpression ConvertFromJsonExtract(SqlExpression expression, Type returnType)
 => returnType == typeof(bool)
         ? _sqlExpressionFactory.Equal(
     expression,
     _sqlExpressionFactory.Constant(true, _typeMappingSource.FindMapping(typeof(bool))))
         : expression;