diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java index f789e2336b9..de860f5a695 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java @@ -235,7 +235,7 @@ protected Builder constructClient(Builder * @param entity The entity to output. * @param The type of entity. */ - protected void output(T entity) { + private void output(T entity) { if (outputFormat == null) { PlainFormat.output(entity, context); return; 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..05bfb492766 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,8 @@ package org.apache.gravitino.cli.commands; -import com.google.common.base.Joiner; +import org.apache.gravitino.Audit; +import org.apache.gravitino.Schema; import org.apache.gravitino.cli.CommandContext; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; @@ -49,8 +50,10 @@ public ListSchema(CommandContext context, String metalake, String catalog) { @Override public void handle() { String[] schemas = new String[0]; + GravitinoClient client; + try { - GravitinoClient client = buildClient(metalake); + client = buildClient(metalake); schemas = client.loadCatalog(catalog).asSchemas().listSchemas(); } catch (NoSuchMetalakeException err) { exitWithError(ErrorMessages.UNKNOWN_METALAKE); @@ -60,8 +63,29 @@ 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 exist."); + return; + } + + Schema[] schemaObjects = new Schema[schemas.length]; + for (int i = 0; i < schemas.length; i++) { + String schemaName = schemas[i]; + Schema gSchema = + new Schema() { + @Override + public String name() { + return schemaName; + } + + @Override + public Audit auditInfo() { + return null; + } + }; + schemaObjects[i] = gSchema; + } - printInformation(all); + printResults(schemaObjects); } } 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..1f098efce77 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,12 @@ package org.apache.gravitino.cli.commands; -import com.google.common.base.Joiner; -import java.util.ArrayList; -import java.util.List; +import org.apache.gravitino.Audit; import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Namespace; import org.apache.gravitino.cli.CommandContext; +import org.apache.gravitino.rel.Column; +import org.apache.gravitino.rel.Table; /** List the names of all tables in a schema. */ public class ListTables extends TableCommand { @@ -49,22 +49,41 @@ public ListTables(CommandContext context, String metalake, String catalog, Strin public void handle() { NameIdentifier[] tables = null; Namespace name = Namespace.of(schema); + try { 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()); + if (tables.length == 0) { + printInformation("No tables exist."); + return; } - String all = - tableNames.isEmpty() - ? "No tables exist." - : Joiner.on(System.lineSeparator()).join(tableNames); + Table[] gTables = new Table[tables.length]; + for (int i = 0; i < tables.length; i++) { + String tableName = tables[i].name(); + gTables[i] = + new Table() { + + @Override + public String name() { + return tableName; + } + + @Override + public Column[] columns() { + return new Column[0]; + } + + @Override + public Audit auditInfo() { + return null; + } + }; + } - printResults(all); + printResults(gTables); } } 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..e5021d62f3b 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,97 @@ 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() == null || 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, "table"); + 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..60e9a50eea7 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; @@ -279,11 +283,14 @@ void testCatalogDetailsWithTableFormat() { void testListCatalogWithTableFormat() { CommandContext mockContext = getMockContext(); Catalog mockCatalog1 = - getMockCatalog("catalog1", Catalog.Type.FILESET, "provider1", "This is a catalog"); + getMockCatalog( + "catalog1", Catalog.Type.RELATIONAL, "demo_provider", "This is a demo catalog"); Catalog mockCatalog2 = - getMockCatalog("catalog2", Catalog.Type.RELATIONAL, "provider2", "This is another catalog"); + getMockCatalog( + "catalog2", Catalog.Type.RELATIONAL, "demo_provider", "This is another demo catalog"); TableFormat.output(new Catalog[] {mockCatalog1, mockCatalog2}, mockContext); + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); Assertions.assertEquals( "+----------+\n" @@ -295,6 +302,77 @@ 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("demo_schema1", "This is a demo schema"); + Schema mockSchema2 = getMockSchema("demo_schema2", "This is another demo schema"); + + TableFormat.output(new Schema[] {mockSchema1, mockSchema2}, mockContext); + + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+--------------+\n" + + "| Schema |\n" + + "+--------------+\n" + + "| demo_schema1 |\n" + + "| demo_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 demo table"); + Table mockTable2 = getMockTable("table2", "This is another demo table"); + TableFormat.output(new Table[] {mockTable1, mockTable2}, mockContext); + + String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim(); + Assertions.assertEquals( + "+--------+\n" + + "| Table |\n" + + "+--------+\n" + + "| table1 |\n" + + "| table2 |\n" + + "+--------+", + output); + } + @Test void testOutputWithUnsupportType() { CommandContext mockContext = getMockContext(); @@ -307,12 +385,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 +417,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; + } }