public virtual SqlExpression Translate([NotNull] Expression expression)
        {
            Check.NotNull(expression, nameof(expression));

            var result = Visit(expression);

            if (result is SqlExpression translation)
            {
                if (translation is SqlUnaryExpression sqlUnaryExpression &&
                    sqlUnaryExpression.OperatorType == ExpressionType.Convert &&
                    sqlUnaryExpression.Type == typeof(object))
                {
                    translation = sqlUnaryExpression.Operand;
                }

                translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

                if ((translation is SqlConstantExpression ||
                     translation is SqlParameterExpression) &&
                    translation.TypeMapping == null)
                {
                    // Non-mappable constant/parameter
                    return(null);
                }

                _sqlTypeMappingVerifyingExpressionVisitor.Visit(translation);

                return(translation);
            }

            return(null);
        }
Example #2
0
        public virtual SqlExpression Translate(
            SqlExpression instance,
            MemberInfo member,
            System.Type returnType)
        {
            var declaringType = member.DeclaringType;

            if (declaringType == typeof(DateTime))
            {
                var memberName = member.Name;
                if (s_datePartMapping.TryGetValue(memberName, out var datePart))
                {
                    var extract = _sqlExpressionFactory.Function(
                        "EXTRACT",
                        new[] { new SpannerTimestampExtractExpression(_sqlExpressionFactory, instance, datePart) },
                        returnType);
                    if (datePart == "DAYOFWEEK")
                    {
                        // Cloud Spanner is 1-based, .NET is 0-based.
                        return(_sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Subtract(extract, _sqlExpressionFactory.Constant(1))));
                    }
                    return(_sqlExpressionFactory.ApplyDefaultTypeMapping(extract));
                }
            }
            return(null);
        }
Example #3
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>
        protected override ShapedQueryExpression TranslateCount(ShapedQueryExpression source, LambdaExpression predicate)
        {
            Check.NotNull(source, nameof(source));

            var selectExpression = (SelectExpression)source.QueryExpression;

            if (selectExpression.IsDistinct ||
                selectExpression.Limit != null ||
                selectExpression.Offset != null)
            {
                return(null);
            }

            if (predicate != null)
            {
                source = TranslateWhere(source, predicate);
                if (source == null)
                {
                    return(null);
                }
            }

            var translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(
                _sqlExpressionFactory.Function("COUNT", new[] { _sqlExpressionFactory.Constant(1) }, typeof(int)));

            var projectionMapping = new Dictionary <ProjectionMember, Expression> {
                { new ProjectionMember(), translation }
            };

            selectExpression.ClearOrdering();
            selectExpression.ReplaceProjectionMapping(projectionMapping);
            return(source.UpdateShaperExpression(new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(int))));
        }
Example #4
0
        public virtual SqlExpression Translate([NotNull] Expression expression)
        {
            Check.NotNull(expression, nameof(expression));

            var result = Visit(expression);

            if (result is SqlExpression translation)
            {
                if (translation is SqlUnaryExpression sqlUnaryExpression &&
                    sqlUnaryExpression.OperatorType == ExpressionType.Convert &&
                    sqlUnaryExpression.Type == typeof(object))
                {
                    translation = sqlUnaryExpression.Operand;
                }

                translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

                if (translation.TypeMapping == null)
                {
                    // The return type is not-mappable hence return null
                    return(null);
                }

                _sqlTypeMappingVerifyingExpressionVisitor.Visit(translation);

                return(translation);
            }

            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>
        protected override ShapedQueryExpression TranslateCount(ShapedQueryExpression source, LambdaExpression predicate)
        {
            var selectExpression = (SelectExpression)source.QueryExpression;

            if (selectExpression.IsDistinct ||
                selectExpression.Limit != null ||
                selectExpression.Offset != null)
            {
                throw new InvalidOperationException(CoreStrings.TranslationFailed(predicate.Print()));
            }

            if (predicate != null)
            {
                source = TranslateWhere(source, predicate);
            }

            var translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(
                _sqlExpressionFactory.Function("COUNT", new[] { _sqlExpressionFactory.Constant(1) }, typeof(int)));

            var projectionMapping = new Dictionary <ProjectionMember, Expression> {
                { new ProjectionMember(), translation }
            };

            selectExpression.ClearOrdering();
            selectExpression.ReplaceProjectionMapping(projectionMapping);
            source.ShaperExpression = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(int));

            return(source);
        }
        public virtual SqlExpression Translate(Expression expression)
        {
            var result = Visit(expression);

            if (result is SqlExpression translation)
            {
                if (translation is SqlUnaryExpression sqlUnaryExpression &&
                    sqlUnaryExpression.OperatorType == ExpressionType.Convert &&
                    sqlUnaryExpression.Type == typeof(object))
                {
                    translation = sqlUnaryExpression.Operand;
                }

                translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

                if (translation is SqlConstantExpression &&
                    translation.TypeMapping == null)
                {
                    // Non-mappable constant
                    return(null);
                }

                _sqlVerifyingExpressionVisitor.Visit(translation);

                return(translation);
            }

            return(null);
        }
Example #7
0
 private SqlExpression TranslateStaticFunction(string function, IReadOnlyList <SqlExpression> arguments, System.Type returnType)
 {
     return(_sqlExpressionFactory.ApplyDefaultTypeMapping(
                _sqlExpressionFactory.Function(
                    function,
                    arguments,
                    returnType)));
 }
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (method == IPAddressParse)
            {
                return(_sqlExpressionFactory.Convert(arguments[0], typeof(IPAddress), _sqlExpressionFactory.FindMapping(typeof(IPAddress))));
            }

            if (method == PhysicalAddressParse)
            {
                return(_sqlExpressionFactory.Convert(arguments[0], typeof(PhysicalAddress), _sqlExpressionFactory.FindMapping(typeof(PhysicalAddress))));
            }

            if (method.DeclaringType != typeof(NpgsqlNetworkExtensions))
            {
                return(null);
            }

            return(method.Name switch
            {
                nameof(NpgsqlNetworkExtensions.LessThan) => _sqlExpressionFactory.LessThan(arguments[1], arguments[2]),
                nameof(NpgsqlNetworkExtensions.LessThanOrEqual) => _sqlExpressionFactory.LessThanOrEqual(arguments[1], arguments[2]),
                nameof(NpgsqlNetworkExtensions.GreaterThanOrEqual) => _sqlExpressionFactory.GreaterThanOrEqual(arguments[1], arguments[2]),
                nameof(NpgsqlNetworkExtensions.GreaterThan) => _sqlExpressionFactory.GreaterThan(arguments[1], arguments[2]),

                nameof(NpgsqlNetworkExtensions.ContainedBy) => BoolReturningOnTwoNetworkTypes("<<"),
                nameof(NpgsqlNetworkExtensions.ContainedByOrEqual) => BoolReturningOnTwoNetworkTypes("<<="),
                nameof(NpgsqlNetworkExtensions.Contains) => BoolReturningOnTwoNetworkTypes(">>"),
                nameof(NpgsqlNetworkExtensions.ContainsOrEqual) => BoolReturningOnTwoNetworkTypes(">>="),
                nameof(NpgsqlNetworkExtensions.ContainsOrContainedBy) => BoolReturningOnTwoNetworkTypes("&&"),

                // TODO: Hack, see #1118
                nameof(NpgsqlNetworkExtensions.BitwiseNot) => new SqlUnaryExpression(ExpressionType.Negate,
                                                                                     arguments[1],
                                                                                     arguments[1].Type,
                                                                                     arguments[1].TypeMapping),

                nameof(NpgsqlNetworkExtensions.BitwiseAnd) => _sqlExpressionFactory.And(arguments[1], arguments[2]),
                nameof(NpgsqlNetworkExtensions.BitwiseOr) => _sqlExpressionFactory.Or(arguments[1], arguments[2]),

                // Add/Subtract accept inet + int, so we can't use the default type mapping inference logic which assumes
                // same-typed operands
                nameof(NpgsqlNetworkExtensions.Add)
                => new SqlBinaryExpression(
                    ExpressionType.Add,
                    _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[1]),
                    _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[2]),
                    arguments[1].Type,
                    arguments[1].TypeMapping),

                nameof(NpgsqlNetworkExtensions.Subtract) when arguments[2].Type == typeof(int)
        public SqlExpression Translate(SelectExpression selectExpression, Expression expression)
        {
            _selectExpression = selectExpression;

            var translation = (SqlExpression)Visit(expression);

            _selectExpression = null;

            translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

            _sqlVerifyingExpressionVisitor.Visit(translation);

            return(translation);
        }
Example #10
0
        /// <inheritdoc />
        public virtual SqlExpression Translate(
            IModel model, SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            Check.NotNull(model, nameof(model));
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));

            var dbFunction = model.FindDbFunction(method);

            if (dbFunction != null)
            {
                return(dbFunction.Translation?.Invoke(
                           arguments.Select(e => _sqlExpressionFactory.ApplyDefaultTypeMapping(e)).ToList())
                       ?? _sqlExpressionFactory.Function(
                           dbFunction.Schema,
                           dbFunction.Name,
                           arguments,
                           nullable: true,
                           argumentsPropagateNullability: arguments.Select(a => false).ToList(),
                           method.ReturnType));
            }

            return(_plugins.Concat(_translators)
                   .Select(t => t.Translate(instance, method, arguments))
                   .FirstOrDefault(t => t != 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)
        {
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));

            if (_methodInfoDataLengthMapping.Contains(method))
            {
                var argument = arguments[1];
                if (argument.TypeMapping == null)
                {
                    argument = _sqlExpressionFactory.ApplyDefaultTypeMapping(argument);
                }

                if (_longReturningTypes.Contains(argument.TypeMapping.StoreType))
                {
                    var result = _sqlExpressionFactory.Function(
                        "DATALENGTH",
                        arguments.Skip(1),
                        nullable: true,
                        argumentsPropagateNullability: new[] { true },
                        typeof(long));

                    return(_sqlExpressionFactory.Convert(result, method.ReturnType.UnwrapNullableType()));
                }

                return(_sqlExpressionFactory.Function(
                           "DATALENGTH",
                           arguments.Skip(1),
                           nullable: true,
                           argumentsPropagateNullability: new[] { true },
                           method.ReturnType.UnwrapNullableType()));
            }

            return(null);
        }
Example #12
0
 public virtual SqlExpression Translate(
     SqlExpression instance,
     MethodInfo method,
     IReadOnlyList <SqlExpression> arguments)
 {
     if (s_supportedMethods.TryGetValue(method, out var sqlFunctionName))
     {
         if (sqlFunctionName == "ROUND")
         {
             // Only rounding mode AwayFromZero can be evaluated server side.
             // Trying to use a different rounding mode in a query will fail.
             if (arguments[arguments.Count - 1] is SqlConstantExpression c &&
                 c.Value is MidpointRounding mode &&
                 mode == MidpointRounding.AwayFromZero)
             {
                 arguments = arguments.ToList().GetRange(0, arguments.Count - 1);
             }
             else
             {
                 return(null);
             }
         }
         return(_sqlExpressionFactory.ApplyDefaultTypeMapping(
                    _sqlExpressionFactory.Function(
                        sqlFunctionName,
                        arguments,
                        method.ReturnType)));
     }
Example #13
0
 private SqlExpression TranslateNoArgFunction(string function, SqlExpression instance, System.Type returnType)
 {
     return(_sqlExpressionFactory.ApplyDefaultTypeMapping(
                _sqlExpressionFactory.Function(
                    function,
                    new[] { instance },
                    returnType)));
 }
        public SqlExpression Translate(Expression expression)
        {
            var translation = (SqlExpression)Visit(expression);

            if (translation is SqlUnaryExpression sqlUnaryExpression &&
                sqlUnaryExpression.OperatorType == ExpressionType.Convert &&
                sqlUnaryExpression.Type == typeof(object))
            {
                translation = sqlUnaryExpression.Operand;
            }

            translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

            _sqlVerifyingExpressionVisitor.Visit(translation);

            return(translation);
        }
Example #15
0
 private SqlExpression TranslateSystemFunction(string function, SqlExpression instance, SqlExpression pattern, Type returnType)
 {
     Check.NotNull(instance, nameof(instance));
     return(_sqlExpressionFactory.ApplyDefaultTypeMapping(
                _sqlExpressionFactory.Function(
                    function,
                    new[] { instance, pattern },
                    returnType)));
 }
Example #16
0
        protected override ShapedQueryExpression TranslateGroupBy(
            ShapedQueryExpression source,
            LambdaExpression keySelector,
            LambdaExpression elementSelector,
            LambdaExpression resultSelector)
        {
            var selectExpression = (SelectExpression)source.QueryExpression;

            selectExpression.PrepareForAggregate();

            var remappedKeySelector = RemapLambdaBody(source, keySelector);

            var translatedKey = TranslateGroupingKey(remappedKeySelector)
                                ?? (remappedKeySelector is ConstantExpression ? remappedKeySelector : null);

            if (translatedKey != null)
            {
                if (elementSelector != null)
                {
                    source = TranslateSelect(source, elementSelector);
                }

                var sqlKeySelector = translatedKey is ConstantExpression
                    ? _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(1))
                    : translatedKey;

                var appliedKeySelector = selectExpression.ApplyGrouping(sqlKeySelector);
                translatedKey = translatedKey is ConstantExpression ? translatedKey : appliedKeySelector;

                source.ShaperExpression = new GroupByShaperExpression(translatedKey, source.ShaperExpression);

                if (resultSelector == null)
                {
                    return(source);
                }

                var keyAccessExpression = Expression.MakeMemberAccess(
                    source.ShaperExpression,
                    source.ShaperExpression.Type.GetTypeInfo().GetMember(nameof(IGrouping <int, int> .Key))[0]);

                var original1 = resultSelector.Parameters[0];
                var original2 = resultSelector.Parameters[1];

                var newResultSelectorBody = new ReplacingExpressionVisitor(
                    new Dictionary <Expression, Expression> {
                    { original1, keyAccessExpression },
                    { original2, source.ShaperExpression }
                }).Visit(resultSelector.Body);

                source.ShaperExpression = _projectionBindingExpressionVisitor.Translate(selectExpression, newResultSelectorBody);

                return(source);
            }

            throw new InvalidOperationException();
        }
        public SqlExpression Translate(Expression expression)
        {
            var translation = (SqlExpression)Visit(expression);

            translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

            _sqlVerifyingExpressionVisitor.Visit(translation);

            return(translation);
        }
Example #18
0
        protected override Expression VisitExtension(Expression extensionExpression)
        {
            if (extensionExpression is SelectExpression selectExpression)
            {
                if (IsZero(selectExpression.Limit) &&
                    IsZero(selectExpression.Offset))
                {
                    return(selectExpression.Update(
                               selectExpression.Projection,
                               selectExpression.Tables,
                               selectExpression.GroupBy.Count > 0
                            ? selectExpression.Predicate
                            : _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(false)),
                               selectExpression.GroupBy,
                               selectExpression.GroupBy.Count > 0
                            ? _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(false))
                            : null,
                               new List <OrderingExpression>(0),
                               limit: null,
                               offset: null));
                }

                bool IsZero(SqlExpression?sqlExpression)
                {
                    switch (sqlExpression)
                    {
                    case SqlConstantExpression constant
                        when constant.Value is int intValue:
                        return(intValue == 0);

                    case SqlParameterExpression parameter:
                        _canCache = false;
                        return(_parameterValues[parameter.Name] is int value && value == 0);

                    default:
                        return(false);
                    }
                }
            }

            return(base.VisitExtension(extensionExpression));
        }
        protected override ShapedQueryExpression TranslateAverage(ShapedQueryExpression source, LambdaExpression selector, Type resultType)
        {
            var selectExpression = (SelectExpression)source.QueryExpression;

            if (selectExpression.Limit != null ||
                selectExpression.Offset != null)
            {
                selectExpression.PushdownIntoSubQuery();
            }

            if (selector != null)
            {
                source = TranslateSelect(source, selector);
            }

            var projection = (SqlExpression)selectExpression.GetProjectionExpression(new ProjectionMember());

            var inputType = projection.Type.UnwrapNullableType();

            if (inputType == typeof(int) ||
                inputType == typeof(long))
            {
                projection = _sqlExpressionFactory.ApplyDefaultTypeMapping(
                    _sqlExpressionFactory.Convert(projection, typeof(double)));
            }

            if (inputType == typeof(float))
            {
                projection = _sqlExpressionFactory.Convert(
                    _sqlExpressionFactory.Function(
                        "AVG", new[] { projection }, typeof(double), null),
                    projection.Type,
                    projection.TypeMapping);
            }
            else
            {
                projection = _sqlExpressionFactory.Function(
                    "AVG", new[] { projection }, projection.Type, projection.TypeMapping);
            }

            return(AggregateResultShaper(source, projection, throwOnNullResult: true, resultType));
        }
 private SqlExpression ConvertToValue(SqlExpression sqlExpression, bool condition)
 => condition
         ? _sqlExpressionFactory.Case(
     new[]
 {
     new CaseWhenClause(
         SimplifyNegatedBinary(sqlExpression),
         _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(true)))
 },
     _sqlExpressionFactory.Constant(false))
         : sqlExpression;
Example #21
0
        public virtual SqlExpression?Translate(
            IModel model,
            SqlExpression?instance,
            MethodInfo method,
            IReadOnlyList <SqlExpression> arguments,
            IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            Check.NotNull(model, nameof(model));
            Check.NotNull(method, nameof(method));
            Check.NotNull(arguments, nameof(arguments));
            Check.NotNull(logger, nameof(logger));

            var dbFunction = model.FindDbFunction(method);

            if (dbFunction != null)
            {
                if (dbFunction.Translation != null)
                {
                    return(dbFunction.Translation.Invoke(
                               arguments.Select(e => _sqlExpressionFactory.ApplyDefaultTypeMapping(e)).ToList()));
                }

                var argumentsPropagateNullability = dbFunction.Parameters.Select(p => p.PropagatesNullability);

                if (dbFunction.IsBuiltIn)
                {
                    return(_sqlExpressionFactory.Function(
                               dbFunction.Name,
                               arguments,
                               dbFunction.IsNullable,
                               argumentsPropagateNullability,
                               method.ReturnType.UnwrapNullableType()));
                }

                return(dbFunction.Schema is null
                    ? _sqlExpressionFactory.Function(
                           dbFunction.Name,
                           arguments,
                           dbFunction.IsNullable,
                           argumentsPropagateNullability,
                           method.ReturnType.UnwrapNullableType())
                    : _sqlExpressionFactory.Function(
                           dbFunction.Schema,
                           dbFunction.Name,
                           arguments,
                           dbFunction.IsNullable,
                           argumentsPropagateNullability,
                           method.ReturnType.UnwrapNullableType()));
            }

            return(_plugins.Concat(_translators)
                   .Select(t => t.Translate(instance, method, arguments, logger))
                   .FirstOrDefault(t => t != null));
        }
        public override SqlExpression TranslateLongCount(Expression expression = null)
        {
            if (expression != null)
            {
                // TODO: Translate Count with predicate for GroupBy
                return(null);
            }

            return(_sqlExpressionFactory.ApplyDefaultTypeMapping(
                       _sqlExpressionFactory.Function("COUNT_BIG", new[] { _sqlExpressionFactory.Fragment("*") }, typeof(long))));
        }
 private Expression ConvertToValue(SqlExpression sqlExpression, bool condition)
 {
     return(condition
         ? _sqlExpressionFactory.Case(new[]
     {
         new CaseWhenClause(
             sqlExpression,
             _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(true)))
     },
                                      _sqlExpressionFactory.Constant(false))
         : sqlExpression);
 }
        /// <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 (_methodInfos.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));
            }

            return(null);
        }
Example #25
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 SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <Microsoft.EntityFrameworkCore.DbLoggerCategory.Query> logger)
        {
            if (_methodInfos.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));
            }

            return(null);
        }
Example #26
0
        public SqlExpression Translate(Expression expression)
        {
            var result = Visit(expression);

            if (result is SqlExpression translation)
            {
                translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

                _sqlVerifyingExpressionVisitor.Visit(translation);

                return(translation);
            }

            return(null);
        }
        /// <inheritdoc />
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (!MethodInfoDatePartMapping.TryGetValue(method, out var datePart))
            {
                return(null);
            }

            var interval = arguments[0];

            if (instance is null || interval is null)
            {
                return(null);
            }

            // Note: ideally we'd simply generate a PostgreSQL interval expression, but the .NET mapping of that is TimeSpan,
            // which does not work for months, years, etc. So we generate special fragments instead.
            if (interval is SqlConstantExpression constantExpression)
            {
                // We generate constant intervals as INTERVAL '1 days'
                if (constantExpression.Type == typeof(double) &&
                    ((double)constantExpression.Value >= int.MaxValue ||
                     (double)constantExpression.Value <= int.MinValue))
                {
                    return(null);
                }

                interval = _sqlExpressionFactory.Fragment(FormattableString.Invariant($"INTERVAL '{constantExpression.Value} {datePart}'"));
            }
            else
            {
                // For non-constants, we can't parameterize INTERVAL '1 days'. Instead, we use CAST($1 || ' days' AS interval).
                // Note that a make_interval() function also exists, but accepts only int (for all fields except for
                // seconds), so we don't use it.
                // Note: we instantiate SqlBinaryExpression manually rather than via sqlExpressionFactory because
                // of the non-standard Add expression (concatenate int with text)
                interval = _sqlExpressionFactory.Convert(
                    new SqlBinaryExpression(
                        ExpressionType.Add,
                        _sqlExpressionFactory.ApplyDefaultTypeMapping(interval),
                        _sqlExpressionFactory.Constant(' ' + datePart, _textMapping),
                        typeof(string),
                        _textMapping),
                    typeof(TimeSpan),
                    _intervalMapping);
            }

            return(_sqlExpressionFactory.Add(instance, interval, instance.TypeMapping));
        }
        /// <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 (_functionMapping.TryGetValue(method, out var functionName))
            {
                var propertyReference = arguments[1];
                if (!(propertyReference is ColumnExpression))
                {
                    throw new InvalidOperationException(SqlServerStrings.InvalidColumnNameForFreeText);
                }

                var typeMapping = propertyReference.TypeMapping;
                var freeText    = propertyReference.Type == arguments[2].Type
                    ? _sqlExpressionFactory.ApplyTypeMapping(arguments[2], typeMapping)
                    : _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[2]);

                var functionArguments = new List <SqlExpression> {
                    propertyReference, freeText
                };

                if (arguments.Count == 4)
                {
                    functionArguments.Add(
                        _sqlExpressionFactory.Fragment($"LANGUAGE {((SqlConstantExpression)arguments[3]).Value}"));
                }

                return(_sqlExpressionFactory.Function(
                           functionName,
                           functionArguments,
                           nullable: true,
                           // TODO: don't propagate for now
                           argumentsPropagateNullability: functionArguments.Select(a => false).ToList(),
                           typeof(bool)));
            }

            return(null);
        }
Example #29
0
        public virtual SqlExpression Translate(IModel model, SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            var dbFunction = model.FindDbFunction(method);

            if (dbFunction != null)
            {
                return(dbFunction.Translation?.Invoke(
                           arguments.Select(e => _sqlExpressionFactory.ApplyDefaultTypeMapping(e)).ToList())
                       ?? _sqlExpressionFactory.Function(
                           dbFunction.Schema,
                           dbFunction.Name,
                           arguments,
                           method.ReturnType));
            }

            return(_plugins.Concat(_translators)
                   .Select(t => t.Translate(instance, method, arguments))
                   .FirstOrDefault(t => t != null));
        }
Example #30
0
        private SqlExpression TranslateInternal(Expression expression)
        {
            var result = Visit(expression);

            if (result is SqlExpression translation)
            {
                translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation);

                if (translation.TypeMapping == null)
                {
                    // The return type is not-mappable hence return null
                    return(null);
                }

                _sqlVerifyingExpressionVisitor.Visit(translation);

                return(translation);
            }

            return(null);
        }