presto-main/src/main/java/io/prestosql/operator/scalar/FormatFunction.java/FormatFunction/valueConverter
class FormatFunction:    private static BiFunction<ConnectorSession, Block, Object> valueConverter(Metadata metadata, Type type, int position)
    {
        if (type.equals(UNKNOWN)) {
            return (session, block) -> null;
        }
        if (type.equals(BOOLEAN)) {
            return (session, block) -> type.getBoolean(block, position);
        }
        if (type.equals(TINYINT) || type.equals(SMALLINT) || type.equals(INTEGER) || type.equals(BIGINT)) {
            return (session, block) -> type.getLong(block, position);
        }
        if (type.equals(REAL)) {
            return (session, block) -> intBitsToFloat(toIntExact(type.getLong(block, position)));
        }
        if (type.equals(DOUBLE)) {
            return (session, block) -> type.getDouble(block, position);
        }
        if (type.equals(DATE)) {
            return (session, block) -> LocalDate.ofEpochDay(type.getLong(block, position));
        }
        if (type.equals(TIMESTAMP_WITH_TIME_ZONE)) {
            return (session, block) -> toZonedDateTime(type.getLong(block, position));
        }
        if (type.equals(TIMESTAMP)) {
            return (session, block) -> toLocalDateTime(session, type.getLong(block, position));
        }
        if (type.equals(TIME)) {
            return (session, block) -> toLocalTime(session, type.getLong(block, position));
        }
        // TODO: support TIME WITH TIME ZONE by making SqlTimeWithTimeZone implement TemporalAccessor
        if (type.equals(JSON)) {
            ResolvedFunction function = metadata.resolveFunction(QualifiedName.of("json_format"), fromTypes(JSON));
            MethodHandle handle = metadata.getScalarFunctionImplementation(function).getMethodHandle();
            return (session, block) -> convertToString(handle, type.getSlice(block, position));
        }
        if (isShortDecimal(type)) {
            int scale = ((DecimalType) type).getScale();
            return (session, block) -> BigDecimal.valueOf(type.getLong(block, position), scale);
        }
        if (isLongDecimal(type)) {
            int scale = ((DecimalType) type).getScale();
            return (session, block) -> new BigDecimal(decodeUnscaledValue(type.getSlice(block, position)), scale);
        }
        if (isVarcharType(type) || isCharType(type)) {
            return (session, block) -> type.getSlice(block, position).toStringUtf8();
        }

        BiFunction<ConnectorSession, Block, Object> function;
        if (type.getJavaType() == long.class) {
            function = (session, block) -> type.getLong(block, position);
        }
        else if (type.getJavaType() == double.class) {
            function = (session, block) -> type.getDouble(block, position);
        }
        else if (type.getJavaType() == boolean.class) {
            function = (session, block) -> type.getBoolean(block, position);
        }
        else if (type.getJavaType() == Slice.class) {
            function = (session, block) -> type.getSlice(block, position);
        }
        else {
            function = (session, block) -> type.getObject(block, position);
        }

        MethodHandle handle = castToVarchar(metadata, type);
        if ((handle == null) || (handle.type().parameterCount() != 1)) {
            throw new PrestoException(NOT_SUPPORTED, "Type not supported for formatting: " + type.getDisplayName());
        }

        return (session, block) -> convertToString(handle, function.apply(session, block));
    }