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 bbc881ce06e..5be4f4fcfe9 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 @@ -19,7 +19,6 @@ package org.apache.gravitino.cli.commands; -import com.google.common.base.Joiner; import org.apache.gravitino.cli.CommandContext; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; @@ -60,8 +59,11 @@ public void handle() { exitWithError(exp.getMessage()); } - String all = schemas.length == 0 ? "No schemas exist." : Joiner.on(",").join(schemas); + if (schemas.length == 0) { + printInformation("No schemas found in catalog " + catalog); + return; + } - printInformation(all); + printResults(schemas); } } 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..642dd45766a 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 { @@ -49,22 +50,32 @@ public ListTables(CommandContext context, String metalake, String catalog, Strin public void handle() { NameIdentifier[] tables = null; Namespace name = Namespace.of(schema); + TableCatalog tableCatalog = null; + try { - tables = tableCatalog().listTables(name); + tableCatalog = tableCatalog(); + tables = tableCatalog.listTables(name); } catch (Exception exp) { exitWithError(exp.getMessage()); } List tableNames = new ArrayList<>(); - for (int i = 0; i < tables.length; i++) { - tableNames.add(tables[i].name()); + for (NameIdentifier table : tables) { + tableNames.add(table.name()); + } + // PERF load table may cause performance issue + Table[] tableObjects = new Table[tableNames.size()]; + int i = 0; + for (NameIdentifier tableIdent : tables) { + tableObjects[i] = tableCatalog.loadTable(tableIdent); + i++; } - String all = - tableNames.isEmpty() - ? "No tables exist." - : Joiner.on(System.lineSeparator()).join(tableNames); + if (tableNames.isEmpty()) { + printInformation("No tables exist."); + return; + } - printResults(all); + printResults(tableObjects); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SchemaDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SchemaDetails.java index b9c530aea5e..560fca20dfd 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SchemaDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SchemaDetails.java @@ -68,7 +68,7 @@ public void handle() { } if (result != null) { - printInformation(result.name() + "," + result.comment()); + printResults(result); } } } 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..23950e7ee93 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,8 @@ public void handle() { exitWithError(exp.getMessage()); } - printInformation(gTable.name() + "," + gTable.comment()); + if (gTable != null) { + 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 6d08f73edf0..f266b81b79c 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 @@ -47,7 +47,9 @@ import java.util.Objects; import org.apache.gravitino.Catalog; 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 @@ -75,6 +77,14 @@ public static void output(Object entity, CommandContext context) { new CatalogTableFormat(context).output((Catalog) entity); } else if (entity instanceof Catalog[]) { new CatalogListTableFormat(context).output((Catalog[]) entity); + } else if (entity instanceof Schema) { + new SchemaTableFormat(context).output((Schema) entity); + } else if (entity instanceof Schema[]) { + new SchemaListTableFormat(context).output((Schema[]) entity); + } else if (entity instanceof Table) { + new TableDetailsTableFormat(context).output((Table) entity); + } else if (entity instanceof Table[]) { + new TableListTableFormat(context).output((Table[]) entity); } else { throw new IllegalArgumentException("Unsupported object type"); } @@ -540,4 +550,96 @@ public String getOutput(Catalog[] catalogs) { return getTableFormat(columnName); } } + + /** + * Formats a single {@link Schema} instance into a two-column table display. Displays catalog + * details including name and comment information. + */ + static final class SchemaTableFormat extends TableFormat { + public SchemaTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Schema schema) { + Column columnName = new Column(context, "schema"); + Column columnComment = new Column(context, "comment"); + + columnName.addCell(schema.name()); + columnComment.addCell(schema.comment()); + + return getTableFormat(columnName, columnComment); + } + } + + /** + * Formats an array of Schemas into a single-column table display. Lists all schema names in a + * vertical format. + */ + static final class SchemaListTableFormat extends TableFormat { + public SchemaListTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Schema[] schemas) { + Column column = new Column(context, "schema"); + Arrays.stream(schemas).forEach(schema -> column.addCell(schema.name())); + + return getTableFormat(column); + } + } + + /** + * Formats a single {@link Table} instance into a two-column table display. Displays table details + * including name and comment information. + */ + static final class TableDetailsTableFormat extends TableFormat { + public TableDetailsTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Table table) { + Column columnName = new Column(context, "name"); + Column columnType = new Column(context, "type"); + Column columnAutoIncrement = new Column(context, "AutoIncrement"); + Column columnNullable = new Column(context, "nullable"); + Column columnComment = new Column(context, "comment"); + + org.apache.gravitino.rel.Column[] columns = table.columns(); + for (org.apache.gravitino.rel.Column column : columns) { + columnName.addCell(column.name()); + columnType.addCell(column.dataType().simpleString()); + columnAutoIncrement.addCell(column.autoIncrement()); + columnNullable.addCell(column.nullable()); + columnComment.addCell(column.comment().isEmpty() ? "N/A" : column.comment()); + } + + return getTableFormat( + columnName, columnType, columnAutoIncrement, columnNullable, columnComment); + } + } + + /** + * Formats an array of {@link Table} into a single-column table display. Lists all table names in + * a vertical format. + */ + static final class TableListTableFormat extends TableFormat { + public TableListTableFormat(CommandContext context) { + super(context); + } + + /** {@inheritDoc} */ + @Override + public String getOutput(Table[] tables) { + Column column = new Column(context, "name"); + Arrays.stream(tables).forEach(table -> column.addCell(table.name())); + + return getTableFormat(column); + } + } } 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 64d5ea4987b..9c6df8e94e3 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 @@ -28,9 +28,13 @@ import java.nio.charset.StandardCharsets; import org.apache.gravitino.Catalog; import org.apache.gravitino.Metalake; +import org.apache.gravitino.Schema; 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.Type; +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; @@ -295,6 +299,75 @@ void testListCatalogWithTableFormat() { output); } + @Test + void testSchemaDetailsWithTableFormat() { + CommandContext mockContext = getMockContext(); + Schema mockSchema = getMockSchema(); + + TableFormat.output(mockSchema, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+-------------+-----------------------+\n" + + "| Schema | Comment |\n" + + "+-------------+-----------------------+\n" + + "| demo_schema | This is a demo schema |\n" + + "+-------------+-----------------------+", + output); + } + + @Test + void testListSchemaWithTableFormat() { + CommandContext mockContext = getMockContext(); + Schema mockSchema1 = getMockSchema("schema1", "This is a schema"); + Schema mockSchema2 = getMockSchema("schema2", "This is another schema"); + + TableFormat.output(new Schema[] {mockSchema1, mockSchema2}, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+---------+\n" + + "| Schema |\n" + + "+---------+\n" + + "| schema1 |\n" + + "| schema2 |\n" + + "+---------+", + 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 | AutoIncrement | Nullable | Comment |\n" + + "+------+---------+---------------+----------+-------------------------+\n" + + "| id | integer | true | false | This is a int column |\n" + + "| name | string | false | true | This is a string 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 testOutputWithUnsupportType() { CommandContext mockContext = getMockContext(); @@ -307,12 +380,6 @@ void testOutputWithUnsupportType() { }); } - private void addRepeatedCells(Column column, int count) { - for (int i = 0; i < count; i++) { - column.addCell(column.getHeader() + "-" + (i + 1)); - } - } - private CommandContext getMockContext() { CommandContext mockContext = mock(CommandContext.class); @@ -345,4 +412,48 @@ private Catalog getMockCatalog(String name, Catalog.Type type, String provider, return mockCatalog; } + + private Schema getMockSchema() { + return getMockSchema("demo_schema", "This is a demo schema"); + } + + private Schema getMockSchema(String name, String comment) { + Schema mockSchema = mock(Schema.class); + when(mockSchema.name()).thenReturn(name); + when(mockSchema.comment()).thenReturn(comment); + + return mockSchema; + } + + private Table getMockTable() { + return getMockTable("demo_table", "This is a demo table"); + } + + private Table getMockTable(String name, String comment) { + Table mockTable = mock(Table.class); + org.apache.gravitino.rel.Column mockColumnInt = + getMockColumn("id", Types.IntegerType.get(), "This is a int column", false, true); + org.apache.gravitino.rel.Column mockColumnString = + getMockColumn("name", Types.StringType.get(), "This is a string column", true, false); + + when(mockTable.name()).thenReturn(name); + when(mockTable.comment()).thenReturn(comment); + when(mockTable.columns()) + .thenReturn(new org.apache.gravitino.rel.Column[] {mockColumnInt, mockColumnString}); + + return mockTable; + } + + private org.apache.gravitino.rel.Column getMockColumn( + String name, Type dataType, String comment, boolean nullable, boolean autoIncrement) { + + org.apache.gravitino.rel.Column mockColumn = mock(org.apache.gravitino.rel.Column.class); + when(mockColumn.name()).thenReturn(name); + when(mockColumn.dataType()).thenReturn(dataType); + when(mockColumn.comment()).thenReturn(comment); + when(mockColumn.nullable()).thenReturn(nullable); + when(mockColumn.autoIncrement()).thenReturn(autoIncrement); + + return mockColumn; + } }