/* * 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 com.facebook.presto.operator.scalar; import com.facebook.presto.annotation.UsedByGeneratedCode; import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlOperator; import com.facebook.presto.server.SliceSerializer; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.util.JsonUtil.JsonGeneratorWriter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import io.airlift.json.ObjectMapperProvider; import io.airlift.slice.DynamicSliceOutput; import io.airlift.slice.Slice; import io.airlift.slice.SliceOutput; import java.io.IOException; import java.lang.invoke.MethodHandle; import java.util.ArrayList; import java.util.List; import static com.facebook.presto.metadata.Signature.withVariadicBound; import static com.facebook.presto.operator.scalar.JsonOperators.JSON_FACTORY; import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static com.facebook.presto.util.Failures.checkCondition; import static com.facebook.presto.util.JsonUtil.JsonGeneratorWriter.createJsonGeneratorWriter; import static com.facebook.presto.util.JsonUtil.canCastToJson; import static com.facebook.presto.util.JsonUtil.createJsonGenerator; import static com.facebook.presto.util.Reflection.methodHandle; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfUnchecked; public class RowToJsonCast extends SqlOperator { public static final RowToJsonCast ROW_TO_JSON = new RowToJsonCast(); private static final Supplier<ObjectMapper> OBJECT_MAPPER = Suppliers.memoize(() -> new ObjectMapperProvider().get().registerModule(new SimpleModule().addSerializer(Slice.class, new SliceSerializer()))); private static final MethodHandle METHOD_HANDLE = methodHandle(RowToJsonCast.class, "toJson", List.class, ConnectorSession.class, Block.class); private RowToJsonCast() { super(OperatorType.CAST, ImmutableList.of(withVariadicBound("T", "row")), ImmutableList.of(), parseTypeSignature(StandardTypes.JSON), ImmutableList.of(parseTypeSignature("T"))); } @Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { checkArgument(arity == 1, "Expected arity to be 1"); Type type = boundVariables.getTypeVariable("T"); checkCondition(canCastToJson(type), INVALID_CAST_ARGUMENT, "Cannot cast %s to JSON", type); List<Type> fieldTypes = type.getTypeParameters(); List<JsonGeneratorWriter> fieldWriters = new ArrayList<>(fieldTypes.size()); for (int i = 0; i < fieldTypes.size(); i++) { fieldWriters.add(createJsonGeneratorWriter(fieldTypes.get(i))); } MethodHandle methodHandle = METHOD_HANDLE.bindTo(fieldWriters); return new ScalarFunctionImplementation(false, ImmutableList.of(false), methodHandle, isDeterministic()); } @UsedByGeneratedCode public static Slice toJson(List<JsonGeneratorWriter> fieldWriters, ConnectorSession session, Block block) { try { SliceOutput output = new DynamicSliceOutput(40); try (JsonGenerator jsonGenerator = createJsonGenerator(JSON_FACTORY, output)) { jsonGenerator.writeStartArray(); for (int i = 0; i < block.getPositionCount(); i++) { fieldWriters.get(i).writeJsonValue(jsonGenerator, block, i, session); } jsonGenerator.writeEndArray(); } return output.slice(); } catch (IOException e) { throwIfUnchecked(e); throw new RuntimeException(e); } } }