All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.operator.scalar.json.JsonArrayFunction Maven / Gradle / Ivy

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.operator.scalar.json;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.NullNode;
import com.google.common.collect.ImmutableList;
import io.trino.annotation.UsedByGeneratedCode;
import io.trino.json.ir.TypedValue;
import io.trino.metadata.SqlScalarFunction;
import io.trino.operator.scalar.ChoicesSpecializedSqlScalarFunction;
import io.trino.operator.scalar.SpecializedSqlScalarFunction;
import io.trino.spi.TrinoException;
import io.trino.spi.block.SqlRow;
import io.trino.spi.function.BoundSignature;
import io.trino.spi.function.FunctionMetadata;
import io.trino.spi.function.Signature;
import io.trino.spi.type.RowType;
import io.trino.spi.type.Type;
import io.trino.spi.type.TypeSignature;
import io.trino.type.Json2016Type;

import java.lang.invoke.MethodHandle;

import static com.google.common.base.Preconditions.checkState;
import static io.trino.json.JsonInputErrorNode.JSON_ERROR;
import static io.trino.json.ir.SqlJsonLiteralConverter.getJsonNode;
import static io.trino.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE;
import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL;
import static io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL;
import static io.trino.spi.type.StandardTypes.BOOLEAN;
import static io.trino.spi.type.StandardTypes.JSON_2016;
import static io.trino.spi.type.TypeUtils.readNativeValue;
import static io.trino.sql.analyzer.ExpressionAnalyzer.JSON_NO_PARAMETERS_ROW_TYPE;
import static io.trino.util.Reflection.methodHandle;

public class JsonArrayFunction
        extends SqlScalarFunction
{
    public static final JsonArrayFunction JSON_ARRAY_FUNCTION = new JsonArrayFunction();
    public static final String JSON_ARRAY_FUNCTION_NAME = "$json_array";
    private static final MethodHandle METHOD_HANDLE = methodHandle(JsonArrayFunction.class, "jsonArray", RowType.class, SqlRow.class, boolean.class);
    private static final JsonNode EMPTY_ARRAY = new ArrayNode(JsonNodeFactory.instance);

    private JsonArrayFunction()
    {
        super(FunctionMetadata.scalarBuilder(JSON_ARRAY_FUNCTION_NAME)
                .signature(Signature.builder()
                        .typeVariable("E")
                        .returnType(new TypeSignature(JSON_2016))
                        .argumentTypes(ImmutableList.of(new TypeSignature("E"), new TypeSignature(BOOLEAN)))
                        .build())
                .argumentNullability(true, false)
                .hidden()
                .description("Creates a JSON array from elements")
                .build());
    }

    @Override
    protected SpecializedSqlScalarFunction specialize(BoundSignature boundSignature)
    {
        RowType elementsRowType = (RowType) boundSignature.getArgumentType(0);
        MethodHandle methodHandle = METHOD_HANDLE
                .bindTo(elementsRowType);
        return new ChoicesSpecializedSqlScalarFunction(
                boundSignature,
                FAIL_ON_NULL,
                ImmutableList.of(BOXED_NULLABLE, NEVER_NULL),
                methodHandle);
    }

    @UsedByGeneratedCode
    public static JsonNode jsonArray(RowType elementsRowType, SqlRow elementsRow, boolean nullOnNull)
    {
        if (JSON_NO_PARAMETERS_ROW_TYPE.equals(elementsRowType)) {
            return EMPTY_ARRAY;
        }

        int rawIndex = elementsRow.getRawIndex();
        ImmutableList.Builder arrayElements = ImmutableList.builder();

        for (int i = 0; i < elementsRowType.getFields().size(); i++) {
            Type elementType = elementsRowType.getFields().get(i).getType();
            Object element = readNativeValue(elementType, elementsRow.getRawFieldBlock(i), rawIndex);
            checkState(!JSON_ERROR.equals(element), "malformed JSON error suppressed in the input function");

            JsonNode elementNode;
            if (element == null) {
                if (nullOnNull) {
                    elementNode = NullNode.getInstance();
                }
                else { // absent on null
                    continue;
                }
            }
            else if (elementType.equals(Json2016Type.JSON_2016)) {
                elementNode = (JsonNode) element;
            }
            else {
                elementNode = getJsonNode(TypedValue.fromValueAsObject(elementType, element))
                        .orElseThrow(() -> new TrinoException(INVALID_FUNCTION_ARGUMENT, "value passed to JSON_ARRAY function cannot be converted to JSON"));
            }

            arrayElements.add(elementNode);
        }

        return new ArrayNode(JsonNodeFactory.instance, arrayElements.build());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy