Beispiel #1
0
        /// <inheritdoc />
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            // Support DateTime subtraction, which returns a TimeSpan.
            if (binaryExpression.NodeType == ExpressionType.Subtract &&
                binaryExpression.Left.Type.UnwrapNullableType() == typeof(DateTime) &&
                binaryExpression.Left.Type.UnwrapNullableType() == typeof(DateTime))
            {
                if (TranslationFailed(binaryExpression.Left, Visit(TryRemoveImplicitConvert(binaryExpression.Left)), out var sqlLeft) ||
                    TranslationFailed(binaryExpression.Right, Visit(TryRemoveImplicitConvert(binaryExpression.Right)), out var sqlRight))
                {
                    return(null);
                }

                var inferredDateTimeTypeMapping = ExpressionExtensions.InferTypeMapping(sqlLeft, sqlRight);
                return(new SqlBinaryExpression(
                           ExpressionType.Subtract,
                           _sqlExpressionFactory.ApplyTypeMapping(sqlLeft, inferredDateTimeTypeMapping),
                           _sqlExpressionFactory.ApplyTypeMapping(sqlRight, inferredDateTimeTypeMapping),
                           typeof(TimeSpan),
                           null));
            }

            if (binaryExpression.NodeType == ExpressionType.ArrayIndex)
            {
                if (TranslationFailed(binaryExpression.Left, Visit(TryRemoveImplicitConvert(binaryExpression.Left)), out var sqlLeft) ||
                    TranslationFailed(binaryExpression.Right, Visit(TryRemoveImplicitConvert(binaryExpression.Right)), out var sqlRight))
                {
                    return(null);
                }

                // ArrayIndex over bytea is special, we have to use function rather than subscript
                if (binaryExpression.Left.Type == typeof(byte[]))
                {
                    return(_sqlExpressionFactory.Function(
                               "get_byte",
                               new[]
                    {
                        _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlLeft),
                        _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlRight)
                    },
                               nullable: true,
                               argumentsPropagateNullability: TrueArrays[2],
                               typeof(byte),
                               _typeMappingSource.FindMapping(typeof(byte))
                               ));
                }

                return
                    // Try translating ArrayIndex inside json column
                    (_jsonPocoTranslator.TranslateMemberAccess(sqlLeft, sqlRight, binaryExpression.Type) ??
                     // Other types should be subscriptable - but PostgreSQL arrays are 1-based, so adjust the index.
                     _sqlExpressionFactory.ArrayIndex(sqlLeft, GenerateOneBasedIndexExpression(sqlRight)));
            }

            return(base.VisitBinary(binaryExpression));
        }
    public virtual SqlExpression?Translate(
        SqlExpression?instance,
        MethodInfo method,
        IReadOnlyList <SqlExpression> arguments,
        IDiagnosticsLogger <DbLoggerCategory.Query> logger)
    {
        if (instance?.Type.IsGenericList() == true && !IsMappedToNonArray(instance))
        {
            // Translate list[i]. Note that array[i] is translated by NpgsqlSqlTranslatingExpressionVisitor.VisitBinary (ArrayIndex)
            if (method.Name == "get_Item" && arguments.Count == 1)
            {
                return
                    // Try translating indexing inside json column
                    (_jsonPocoTranslator.TranslateMemberAccess(instance, arguments[0], method.ReturnType) ??
                     // Other types should be subscriptable - but PostgreSQL arrays are 1-based, so adjust the index.
                     _sqlExpressionFactory.ArrayIndex(instance, GenerateOneBasedIndexExpression(arguments[0])));
            }

            return(TranslateCommon(instance, arguments));
        }

        if (instance is null && arguments.Count > 0 && arguments[0].Type.IsArrayOrGenericList() && !IsMappedToNonArray(arguments[0]))
        {
            // Extension method over an array or list
            if (method.IsClosedFormOf(Enumerable_SequenceEqual) && arguments[1].Type.IsArray)
            {
                return(_sqlExpressionFactory.Equal(arguments[0], arguments[1]));
            }

            return(TranslateCommon(arguments[0], arguments.Slice(1)));
        }

        if (method.DeclaringType == typeof(string) &&
            (method == String_Join1 ||
             method == String_Join2 ||
             method == String_Join3 ||
             method == String_Join4 ||
             method.IsClosedFormOf(String_Join_generic1) ||
             method.IsClosedFormOf(String_Join_generic2)) &&
            !IsMappedToNonArray(arguments[0]))
        {
            return(_sqlExpressionFactory.Function(
                       "array_to_string",
                       new[] { arguments[1], arguments[0], _sqlExpressionFactory.Constant("") },
                       nullable: true,
                       argumentsPropagateNullability: TrueArrays[3],
                       typeof(string)));
        }

        // Not an array/list
        return(null);