diff --git a/shell/src/main/java/org/apache/accumulo/shell/commands/ActiveCompactionHelper.java b/shell/src/main/java/org/apache/accumulo/shell/commands/ActiveCompactionHelper.java index 62dd93f2697..fa93483d240 100644 --- a/shell/src/main/java/org/apache/accumulo/shell/commands/ActiveCompactionHelper.java +++ b/shell/src/main/java/org/apache/accumulo/shell/commands/ActiveCompactionHelper.java @@ -31,8 +31,11 @@ import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.accumulo.core.client.admin.ActiveCompaction; import org.apache.accumulo.core.client.admin.InstanceOperations; +import org.apache.accumulo.core.data.TabletId; import org.apache.accumulo.core.util.DurationFormat; +import org.apache.accumulo.core.util.TextUtil; import org.apache.accumulo.shell.Shell; +import org.apache.hadoop.io.Text; class ActiveCompactionHelper { @@ -96,13 +99,63 @@ private static String formatActiveCompactionLine(ActiveCompaction ac) { return String.format( "%21s | %9s | %5s | %6s | %5s | %5s | %15s | %-40s | %5s | %35s | %9s | %s", host, dur, ac.getType(), ac.getReason(), shortenCount(ac.getEntriesRead()), - shortenCount(ac.getEntriesWritten()), ac.getTable(), ac.getTablet(), + shortenCount(ac.getEntriesWritten()), ac.getTable(), formatTablet(ac.getTablet()), ac.getInputFiles().size(), output, iterList, iterOpts); } catch (TableNotFoundException e) { return "ERROR " + e.getMessage(); } } + private static String formatTablet(TabletId tabletId) { + if (tabletId == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + appendEscapedTableId(sb, tabletId.getTable().canonical()); + appendTabletRow(sb, tabletId.getEndRow()); + appendTabletRow(sb, tabletId.getPrevEndRow()); + return sb.toString(); + } + + private static void appendEscapedTableId(StringBuilder sb, String tableId) { + for (int i = 0; i < tableId.length(); i++) { + char c = tableId.charAt(i); + if (c == '\\') { + sb.append("\\\\"); + } else if (c == ';') { + sb.append("\\;"); + } else { + sb.append(c); + } + } + } + + private static void appendTabletRow(StringBuilder sb, Text row) { + if (row == null) { + sb.append("<"); + return; + } + sb.append(';'); + Text truncated = TextUtil.truncate(row); + byte[] bytes = TextUtil.getBytes(truncated); + appendEscapedBytes(sb, bytes); + } + + private static void appendEscapedBytes(StringBuilder sb, byte[] bytes) { + for (byte b : bytes) { + int c = b & 0xFF; + if (c == '\\') { + sb.append("\\\\"); + } else if (c == ';') { + sb.append("\\;"); + } else if (c >= 32 && c <= 126) { + sb.append((char) c); + } else { + sb.append("\\x").append(String.format("%02X", c)); + } + } + } + public static Stream appendHeader(Stream stream) { Stream header = Stream.of(String.format( " %-21s| %-9s | %-5s | %-6s | %-5s | %-5s | %-15s | %-40s | %-5s | %-35s | %-9s | %s", diff --git a/test/src/main/java/org/apache/accumulo/test/shell/ShellServerIT.java b/test/src/main/java/org/apache/accumulo/test/shell/ShellServerIT.java index c2755581869..00e7cfdc033 100644 --- a/test/src/main/java/org/apache/accumulo/test/shell/ShellServerIT.java +++ b/test/src/main/java/org/apache/accumulo/test/shell/ShellServerIT.java @@ -1447,6 +1447,7 @@ public void listcompactions() throws Exception { final String table = getUniqueNames(1)[0]; ts.exec("createtable " + table, true); + ts.exec("addsplits -t " + table + " a\0test", true); ts.exec( "config -t " + table + " -s table.iterator.minc.slow=30,org.apache.accumulo.test.functional.SlowIterator", @@ -1460,8 +1461,11 @@ public void listcompactions() throws Exception { ts.exec("sleep 0.2", true); ts.exec("listcompactions", true, "default_tablet"); String[] lines = ts.output.get().split("\n"); - String last = lines[lines.length - 1]; - String[] parts = last.split("\\|"); + String compaction = Arrays.stream(lines).filter(line -> line.contains("default_tablet")) + .findFirst().orElseThrow(); + assertTrue(compaction.contains("\\x00"), + "Expected tablet to display \\x00 for null byte: " + compaction); + String[] parts = compaction.split("\\|"); assertEquals(12, parts.length); ts.exec("deletetable -f " + table, true); }