Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Markdown output format to the CLI #17697

Merged
merged 2 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,21 @@
*/
package io.trino.cli;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.trino.client.Column;
import io.trino.client.Row;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.repeat;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.partition;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.io.BaseEncoding.base16;
import static io.trino.cli.FormatUtils.formatValue;
import static io.trino.client.ClientStandardTypes.BIGINT;
import static io.trino.client.ClientStandardTypes.DECIMAL;
import static io.trino.client.ClientStandardTypes.DOUBLE;
Expand All @@ -43,7 +38,6 @@
import static java.lang.Math.max;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static org.jline.utils.AttributedString.stripAnsi;
import static org.jline.utils.WCWidth.wcwidth;

Expand All @@ -53,9 +47,6 @@ public class AlignedTablePrinter
private static final Set<String> NUMERIC_TYPES = ImmutableSet.of(TINYINT, SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, DECIMAL);

private static final Splitter LINE_SPLITTER = Splitter.on('\n');
private static final Splitter HEX_SPLITTER = Splitter.fixedLength(2);
private static final Joiner HEX_BYTE_JOINER = Joiner.on(' ');
private static final Joiner HEX_LINE_JOINER = Joiner.on('\n');

private final List<String> fieldNames;
private final List<Boolean> numericFields;
Expand Down Expand Up @@ -156,87 +147,6 @@ public void printRows(List<List<?>> rows, boolean complete)
writer.flush();
}

static String formatValue(Object o)
{
if (o == null) {
return "NULL";
}

if (o instanceof Map) {
return formatMap((Map<?, ?>) o);
}

if (o instanceof List) {
return formatList((List<?>) o);
}

if (o instanceof Row) {
return formatRow(((Row) o));
}

if (o instanceof byte[]) {
return formatHexDump((byte[]) o, 16);
}

return o.toString();
}

private static String formatHexDump(byte[] bytes, int bytesPerLine)
{
// hex pairs: ["61", "62", "63"]
Iterable<String> hexPairs = createHexPairs(bytes);

// hex lines: [["61", "62", "63], [...]]
Iterable<List<String>> hexLines = partition(hexPairs, bytesPerLine);

// lines: ["61 62 63", ...]
Iterable<String> lines = transform(hexLines, HEX_BYTE_JOINER::join);

// joined: "61 62 63\n..."
return HEX_LINE_JOINER.join(lines);
}

static String formatHexDump(byte[] bytes)
{
return HEX_BYTE_JOINER.join(createHexPairs(bytes));
}

private static Iterable<String> createHexPairs(byte[] bytes)
{
// hex dump: "616263"
String hexDump = base16().lowerCase().encode(bytes);

// hex pairs: ["61", "62", "63"]
return HEX_SPLITTER.split(hexDump);
}

static String formatList(List<? extends Object> list)
{
return list.stream()
.map(AlignedTablePrinter::formatValue)
.collect(joining(", ", "[", "]"));
}

static String formatMap(Map<? extends Object, ? extends Object> map)
{
return map.entrySet().stream()
.map(entry -> format("%s=%s", formatValue(entry.getKey()), formatValue(entry.getValue())))
.collect(joining(", ", "{", "}"));
}

static String formatRow(Row row)
{
return row.getFields().stream()
.map(field -> {
String formattedValue = formatValue(field.getValue());
if (field.getName().isPresent()) {
return format("%s=%s", formatValue(field.getName().get()), formattedValue);
}
return formattedValue;
})
.collect(joining(", ", "{", "}"));
}

private static String center(String s, int maxWidth, int padding)
{
int width = consoleWidth(s);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ public enum OutputFormat
CSV_UNQUOTED,
CSV_HEADER_UNQUOTED,
JSON,
MARKDOWN,
NULL
}

Expand Down
34 changes: 2 additions & 32 deletions client/trino-cli/src/main/java/io/trino/cli/CsvPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@

import au.com.bytecode.opencsv.CSVWriter;
import com.google.common.collect.ImmutableList;
import io.trino.client.Row;

import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Map;

import static io.trino.cli.AlignedTablePrinter.formatHexDump;
import static io.trino.cli.AlignedTablePrinter.formatList;
import static io.trino.cli.AlignedTablePrinter.formatMap;
import static io.trino.cli.AlignedTablePrinter.formatRow;
import static io.trino.cli.FormatUtils.formatValue;
import static java.util.Objects.requireNonNull;

public class CsvPrinter
Expand Down Expand Up @@ -114,33 +109,8 @@ private static String[] toStrings(List<?> values, String[] array)
array = new String[rowSize];
}
for (int i = 0; i < rowSize; i++) {
array[i] = formatValue(values.get(i));
array[i] = formatValue(values.get(i), "", -1);
}
return array;
}

static String formatValue(Object o)
{
if (o == null) {
return "";
}

if (o instanceof Map) {
return formatMap((Map<?, ?>) o);
}

if (o instanceof List) {
return formatList((List<?>) o);
}

if (o instanceof Row) {
return formatRow((Row) o);
}

if (o instanceof byte[]) {
return formatHexDump((byte[]) o);
}

return o.toString();
}
}
102 changes: 102 additions & 0 deletions client/trino-cli/src/main/java/io/trino/cli/FormatUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,36 @@
*/
package io.trino.cli;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.primitives.Ints;
import io.airlift.units.DataSize;
import io.airlift.units.Duration;
import io.trino.client.Row;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Map;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.repeat;
import static com.google.common.collect.Iterables.partition;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.io.BaseEncoding.base16;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.joining;

public final class FormatUtils
{
private static final Splitter HEX_SPLITTER = Splitter.fixedLength(2);
private static final Joiner HEX_BYTE_JOINER = Joiner.on(' ');
private static final Joiner HEX_LINE_JOINER = Joiner.on('\n');

private FormatUtils() {}

public static String formatCount(long count)
Expand Down Expand Up @@ -238,4 +251,93 @@ private static int ceil(int dividend, int divisor)
{
return ((dividend + divisor) - 1) / divisor;
}

static String formatValue(Object o)
{
return formatValue(o, "NULL", 16);
}

static String formatValue(Object o, String nullValue, int bytesPerLine)
{
if (o == null) {
return nullValue;
}

if (o instanceof Map) {
return formatMap((Map<?, ?>) o);
}

if (o instanceof List) {
return formatList((List<?>) o);
}

if (o instanceof Row) {
return formatRow(((Row) o));
}

if (o instanceof byte[]) {
return formatHexDump((byte[]) o, bytesPerLine);
}

return o.toString();
}

private static String formatHexDump(byte[] bytes, int bytesPerLine)
{
if (bytesPerLine <= 0) {
return formatHexDump(bytes);
}
// hex pairs: ["61", "62", "63"]
Iterable<String> hexPairs = createHexPairs(bytes);

// hex lines: [["61", "62", "63], [...]]
Iterable<List<String>> hexLines = partition(hexPairs, bytesPerLine);

// lines: ["61 62 63", ...]
Iterable<String> lines = transform(hexLines, HEX_BYTE_JOINER::join);

// joined: "61 62 63\n..."
return HEX_LINE_JOINER.join(lines);
}

static String formatHexDump(byte[] bytes)
{
return HEX_BYTE_JOINER.join(createHexPairs(bytes));
}

private static Iterable<String> createHexPairs(byte[] bytes)
{
// hex dump: "616263"
String hexDump = base16().lowerCase().encode(bytes);

// hex pairs: ["61", "62", "63"]
return HEX_SPLITTER.split(hexDump);
}

static String formatList(List<? extends Object> list)
{
return list.stream()
.map(FormatUtils::formatValue)
.collect(joining(", ", "[", "]"));
}

static String formatMap(Map<? extends Object, ? extends Object> map)
{
return map.entrySet().stream()
.map(entry -> format("%s=%s", formatValue(entry.getKey()), formatValue(entry.getValue())))
.collect(joining(", ", "{", "}"));
}

static String formatRow(Row row)
{
return row.getFields().stream()
.map(field -> {
String formattedValue = formatValue(field.getValue());
if (field.getName().isPresent()) {
return format("%s=%s", formatValue(field.getName().get()), formattedValue);
}
return formattedValue;
})
.collect(joining(", ", "{", "}"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import java.io.Writer;
import java.util.List;

import static io.trino.cli.AlignedTablePrinter.formatHexDump;
import static io.trino.cli.FormatUtils.formatHexDump;
import static java.util.Objects.requireNonNull;

public class JsonPrinter
Expand Down
Loading