Example #1
0
        public virtual SqlExpression?TranslateArrayLength(SqlExpression expression)
        {
            if (expression is ColumnExpression columnExpression &&
                columnExpression.TypeMapping is NpgsqlJsonTypeMapping mapping)
            {
                return(_sqlExpressionFactory.Function(
                           mapping.IsJsonb ? "jsonb_array_length" : "json_array_length",
                           new[] { expression },
                           nullable: true,
                           argumentsPropagateNullability: TrueArrays[2],
                           typeof(int)));
            }

            if (expression is PostgresJsonTraversalExpression traversal)
            {
                // The traversal expression has ReturnsText=true (e.g. ->> not ->), so we recreate it to return
                // the JSON object instead.
                var lastPathComponent = traversal.Path.Last();
                var newTraversal      = new PostgresJsonTraversalExpression(
                    traversal.Expression, traversal.Path,
                    returnsText: false,
                    lastPathComponent.Type,
                    _typeMappingSource.FindMapping(lastPathComponent.Type));

                var jsonMapping = (NpgsqlJsonTypeMapping)traversal.Expression.TypeMapping !;
                return(_sqlExpressionFactory.Function(
                           jsonMapping.IsJsonb ? "jsonb_array_length" : "json_array_length",
                           new[] { newTraversal },
                           nullable: true,
                           argumentsPropagateNullability: TrueArrays[2],
                           typeof(int)));
            }

            return(null);
        }
Example #2
0
    public virtual SqlExpression?Translate(
        SqlExpression?instance,
        MethodInfo method,
        IReadOnlyList <SqlExpression> arguments,
        IDiagnosticsLogger <DbLoggerCategory.Query> logger)
    {
        if (method.DeclaringType != typeof(JsonElement) ||
            instance?.TypeMapping is not NpgsqlJsonTypeMapping mapping)
        {
            return(null);
        }

        // The root of the JSON expression is a ColumnExpression. We wrap that with an empty traversal
        // expression (col #>> '{}'); subsequent traversals will gradually append the path into that.
        // Note that it's possible to call methods such as GetString() directly on the root, and the
        // empty traversal is necessary to properly convert it to a text.
        instance = instance is ColumnExpression columnExpression
            ? _sqlExpressionFactory.JsonTraversal(
            columnExpression, returnsText : false, typeof(string), mapping)
            : instance;

        if (method == GetProperty || method == ArrayIndexer)
        {
            return(instance is PostgresJsonTraversalExpression prevPathTraversal
                ? prevPathTraversal.Append(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0]))
                : null);
        }

        if (GetMethods.Contains(method.Name) &&
            arguments.Count == 0 &&
            instance is PostgresJsonTraversalExpression traversal)
        {
            var traversalToText = new PostgresJsonTraversalExpression(
                traversal.Expression,
                traversal.Path,
                returnsText: true,
                typeof(string),
                _stringTypeMapping);

            return(method.Name == nameof(JsonElement.GetString)
                ? traversalToText
                : ConvertFromText(traversalToText, method.ReturnType));
        }

        if (method == GetArrayLength)
        {
            return(_sqlExpressionFactory.Function(
                       mapping.IsJsonb ? "jsonb_array_length" : "json_array_length",
                       new[] { instance },
                       nullable: true,
                       argumentsPropagateNullability: TrueArrays[1],
                       typeof(int)));
        }

        if (method.Name.StartsWith("TryGet", StringComparison.Ordinal) && arguments.Count == 0)
        {
            throw new InvalidOperationException($"The TryGet* methods on {nameof(JsonElement)} aren't translated yet, use Get* instead.'");
        }

        return(null);
    }
        public SqlExpression?Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (typeof(JToken).IsAssignableFrom(method.DeclaringType) &&
                method.Name == "get_Item" &&
                arguments.Count == 1)
            {
                return((instance is ColumnExpression columnExpression
                        ? _sqlExpressionFactory.JsonTraversal(
                            columnExpression, returnsText: false, typeof(string), instance.TypeMapping)
                        : instance) is PostgresJsonTraversalExpression prevPathTraversal
                        ? prevPathTraversal.Append(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0]))
                        : null);
            }
            if (arguments.FirstOrDefault() is PostgresJsonTraversalExpression traversal)
            {
                // Support for .Value<T>() and .Value<U, T>():
                if (instance == null &&
                    method.Name == nameof(Extensions.Value) &&
                    method.DeclaringType == typeof(Extensions) &&
                    method.IsGenericMethod &&
                    method.GetParameters().Length == 1 &&
                    arguments.Count == 1)
                {
                    var traversalToText = new PostgresJsonTraversalExpression(
                        traversal.Expression,
                        traversal.Path,
                        returnsText: true,
                        typeof(string),
                        _stringTypeMapping);

                    if (method.ReturnType == typeof(string))
                    {
                        return(traversalToText);
                    }
                    else
                    {
                        return(_sqlExpressionFactory.Convert(traversalToText, method.ReturnType, _typeMappingSource.FindMapping(method.ReturnType)));
                    }
                }

                // Support for Count()
                if (instance == null &&
                    method.Name == nameof(Enumerable.Count) &&
                    method.DeclaringType == typeof(Enumerable) &&
                    method.IsGenericMethod &&
                    method.GetParameters().Length == 1 &&
                    arguments.Count == 1)
                {
                    return(_jsonPocoTranslator.TranslateArrayLength(traversal));
                }

                // Predicate-less Any - translate to a simple length check.
                if (method.IsClosedFormOf(_enumerableAnyWithoutPredicate) &&
                    arguments.Count == 1 &&
                    arguments[0].Type.TryGetElementType(out _) &&
                    arguments[0].TypeMapping is NpgsqlJsonTypeMapping)
                {
                    return(_sqlExpressionFactory.GreaterThan(
                               _jsonPocoTranslator.TranslateArrayLength(arguments[0]),
                               _sqlExpressionFactory.Constant(0)));
                }
            }
            return(null);
        }