From 663c3381f6797522223e42c70791322af4f9ffb5 Mon Sep 17 00:00:00 2001 From: pancx Date: Sun, 16 Feb 2025 11:53:39 +0800 Subject: [PATCH] [#5746] improve(CLI): Support table format output for Audit command Add Table format. --- .../gravitino/cli/commands/ListSchema.java | 1 + .../gravitino/cli/commands/ListTables.java | 29 +++++---- .../gravitino/cli/commands/TableDetails.java | 2 +- .../gravitino/cli/outputs/TableFormat.java | 56 +++++++++++++++++ .../gravitino/cli/output/TestPlainFormat.java | 55 ++++++++++++++++ .../gravitino/cli/output/TestTableFormat.java | 62 +++++++++++++++++++ 6 files changed, 191 insertions(+), 14 deletions(-) diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java index ba987486b68..3976d6bd392 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java @@ -53,6 +53,7 @@ public ListSchema(CommandContext context, String metalake, String catalog) { public void handle() { List schemas = Lists.newArrayList(); try { + // There may be a performance overhead GravitinoClient client = buildClient(metalake); SupportsSchemas gObject = client.loadCatalog(catalog).asSchemas(); for (String schemaName : gObject.listSchemas()) { diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java index e1389ed9d1a..b10cf2de8c7 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java @@ -19,12 +19,13 @@ package org.apache.gravitino.cli.commands; -import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.List; import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Namespace; import org.apache.gravitino.cli.CommandContext; +import org.apache.gravitino.rel.Table; +import org.apache.gravitino.rel.TableCatalog; /** List the names of all tables in a schema. */ public class ListTables extends TableCommand { @@ -47,24 +48,26 @@ public ListTables(CommandContext context, String metalake, String catalog, Strin /** List the names of all tables in a schema. */ @Override public void handle() { - NameIdentifier[] tables = null; + NameIdentifier[] tables; Namespace name = Namespace.of(schema); + TableCatalog tableCatalog; + List tablesList = new ArrayList<>(); + try { - tables = tableCatalog().listTables(name); + // There may be a performance overhead + tableCatalog = tableCatalog(); + tables = tableCatalog.listTables(name); + for (NameIdentifier table : tables) { + tablesList.add(tableCatalog.loadTable(table)); + } } catch (Exception exp) { exitWithError(exp.getMessage()); } - List tableNames = new ArrayList<>(); - for (int i = 0; i < tables.length; i++) { - tableNames.add(tables[i].name()); + if (tablesList.isEmpty()) { + printResults("No tables exist."); + } else { + printResults(tablesList.toArray(new Table[0])); } - - String all = - tableNames.isEmpty() - ? "No tables exist." - : Joiner.on(System.lineSeparator()).join(tableNames); - - printResults(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TableDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TableDetails.java index 8ac9fb4fb63..6dc8a4ac40c 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TableDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TableDetails.java @@ -57,6 +57,6 @@ public void handle() { exitWithError(exp.getMessage()); } - printInformation(gTable.name() + "," + gTable.comment()); + printResults(gTable); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java index 418eca471cd..6027c7a1b1c 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java @@ -52,6 +52,7 @@ import org.apache.gravitino.Metalake; import org.apache.gravitino.Schema; import org.apache.gravitino.cli.CommandContext; +import org.apache.gravitino.rel.Table; /** * Abstract base class for formatting entity information into ASCII-art tables. Provides @@ -88,6 +89,10 @@ public static void output(Object entity, CommandContext context) { new SchemaTableFormat(context).output((Schema) entity); } else if (entity instanceof Schema[]) { new SchemasTableFormat(context).output((Schema[]) entity); + } else if (entity instanceof Table) { + new TableTableFormat(context).output((Table) entity); + } else if (entity instanceof Table[]) { + new TablesTableFormat(context).output((Table[]) entity); } else if (entity instanceof Audit) { new AuditTableFormat(context).output((Audit) entity); } else { @@ -657,6 +662,57 @@ public String getOutput(Schema[] schemas) { } } + /** + * Formats a single Table instance into a three-column table display. Displays table details + * including column-name, column-type, and column-comment information. + */ + static final class TableTableFormat extends TableFormat
{ + public TableTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Table entity) { + Column columnA = new Column(context, "NAME"); + Column columnB = new Column(context, "TYPE"); + Column columnC = new Column(context, "COMMENT"); + + for (org.apache.gravitino.rel.Column column : entity.columns()) { + columnA.addCell(column.name()); + columnB.addCell(column.dataType().simpleString()); + columnC.addCell(column.comment()); + } + + return getTableFormat(columnA, columnB, columnC); + } + } + + /** + * Formats an array of Tables into a single-column table display. Lists all table names in a + * vertical format. + */ + static final class TablesTableFormat extends TableFormat { + public TablesTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Table[] entities) { + if (entities.length == 0) { + return null; + } else { + Column columnName = new Column(context, "NAME"); + for (Table table : entities) { + columnName.addCell(table.name()); + } + + return getTableFormat(columnName); + } + } + } + static final class AuditTableFormat extends TableFormat { public AuditTableFormat(CommandContext context) { super(context); diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestPlainFormat.java b/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestPlainFormat.java index e38609ffc89..02790b863c8 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestPlainFormat.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestPlainFormat.java @@ -33,6 +33,8 @@ import org.apache.gravitino.Schema; import org.apache.gravitino.cli.CommandContext; import org.apache.gravitino.cli.outputs.PlainFormat; +import org.apache.gravitino.rel.Table; +import org.apache.gravitino.rel.types.Types; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -122,6 +124,31 @@ void testListSchemaWithPlainFormat() { Assertions.assertEquals("schema1\n" + "schema2", output); } + @Test + void testTableDetailsWithPlainFormat() { + CommandContext mockContext = getMockContext(); + Table mockTable = getMockTable(); + PlainFormat.output(mockTable, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "demo_table\n" + + "col1, boolean, This is a column\n" + + "col2, string, This is another column\n" + + "This is a table", + output); + } + + @Test + void testListTableWithPlainFormat() { + CommandContext mockContext = getMockContext(); + Table mockTable1 = getMockTable("table1", "This is a table"); + Table mockTable2 = getMockTable("table2", "This is another table"); + + PlainFormat.output(new Table[] {mockTable1, mockTable2}, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals("table1\n" + "table2", output); + } + @Test void testAuditWithPlainFormat() { CommandContext mockContext = getMockContext(); @@ -192,6 +219,34 @@ private Schema getMockSchema(String name, String comment) { return mockSchema; } + private Table getMockTable() { + Table mockTable = mock(Table.class); + when(mockTable.name()).thenReturn("demo_table"); + when(mockTable.comment()).thenReturn("This is a table"); + org.apache.gravitino.rel.Column mockColumn1 = mock(org.apache.gravitino.rel.Column.class); + when(mockColumn1.name()).thenReturn("col1"); + when(mockColumn1.dataType()).thenReturn(Types.BooleanType.get()); + when(mockColumn1.comment()).thenReturn("This is a column"); + + org.apache.gravitino.rel.Column mockColumn2 = mock(org.apache.gravitino.rel.Column.class); + when(mockColumn2.name()).thenReturn("col2"); + when(mockColumn2.dataType()).thenReturn(Types.StringType.get()); + when(mockColumn2.comment()).thenReturn("This is another column"); + + when(mockTable.columns()) + .thenReturn(new org.apache.gravitino.rel.Column[] {mockColumn1, mockColumn2}); + + return mockTable; + } + + private Table getMockTable(String name, String comment) { + Table mockTable = mock(Table.class); + when(mockTable.name()).thenReturn(name); + when(mockTable.comment()).thenReturn(comment); + + return mockTable; + } + private Audit getMockAudit() { Audit mockAudit = mock(Audit.class); when(mockAudit.creator()).thenReturn("demo_user"); diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestTableFormat.java b/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestTableFormat.java index 599d9eb4897..528bd520fb7 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestTableFormat.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/output/TestTableFormat.java @@ -34,6 +34,8 @@ import org.apache.gravitino.cli.CommandContext; import org.apache.gravitino.cli.outputs.Column; import org.apache.gravitino.cli.outputs.TableFormat; +import org.apache.gravitino.rel.Table; +import org.apache.gravitino.rel.types.Types; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -365,6 +367,40 @@ void testListSchemaWithTableFormat() { output); } + @Test + void testTableDetailsWithTableFormat() { + CommandContext mockContext = getMockContext(); + Table mockTable = getMockTable(); + TableFormat.output(mockTable, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+------+---------+------------------------+\n" + + "| NAME | TYPE | COMMENT |\n" + + "+------+---------+------------------------+\n" + + "| col1 | boolean | This is a column |\n" + + "| col2 | string | This is another column |\n" + + "+------+---------+------------------------+", + output); + } + + @Test + void testListTableWithTableFormat() { + CommandContext mockContext = getMockContext(); + Table mockTable1 = getMockTable("table1", "This is a table"); + Table mockTable2 = getMockTable("table2", "This is another table"); + + TableFormat.output(new Table[] {mockTable1, mockTable2}, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+--------+\n" + + "| NAME |\n" + + "+--------+\n" + + "| table1 |\n" + + "| table2 |\n" + + "+--------+", + output); + } + @Test void testAuditWithTableFormat() { CommandContext mockContext = getMockContext(); @@ -444,6 +480,32 @@ private Schema getMockSchema(String name, String comment) { return mockSchema; } + private Table getMockTable() { + Table mockTable = mock(Table.class); + org.apache.gravitino.rel.Column mockColumn1 = mock(org.apache.gravitino.rel.Column.class); + when(mockColumn1.name()).thenReturn("col1"); + when(mockColumn1.dataType()).thenReturn(Types.BooleanType.get()); + when(mockColumn1.comment()).thenReturn("This is a column"); + + org.apache.gravitino.rel.Column mockColumn2 = mock(org.apache.gravitino.rel.Column.class); + when(mockColumn2.name()).thenReturn("col2"); + when(mockColumn2.dataType()).thenReturn(Types.StringType.get()); + when(mockColumn2.comment()).thenReturn("This is another column"); + + when(mockTable.columns()) + .thenReturn(new org.apache.gravitino.rel.Column[] {mockColumn1, mockColumn2}); + + return mockTable; + } + + private Table getMockTable(String name, String comment) { + Table mockTable = mock(Table.class); + when(mockTable.name()).thenReturn(name); + when(mockTable.comment()).thenReturn(comment); + + return mockTable; + } + private Audit getMockAudit() { Audit mockAudit = mock(Audit.class); when(mockAudit.creator()).thenReturn("demo_user");