This is an automated email from the ASF dual-hosted git repository.

jmclean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new fb3d900ff0 [#6493] feat(CLI): Support table format output for Schema 
and Table command (#6495)
fb3d900ff0 is described below

commit fb3d900ff0e70514f86dc8462ba07e5611bc4015
Author: Lord of Abyss <103809695+abyss-l...@users.noreply.github.com>
AuthorDate: Mon Feb 24 17:01:29 2025 +0800

    [#6493] feat(CLI): Support table format output for Schema and Table command 
(#6495)
    
    ### What changes were proposed in this pull request?
    
    Support table format output for Schema and Table command.
    
    ### Why are the changes needed?
    
    Fix: #6493
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    local test.
    
    ```bash
    gcli schema list -m demo_metalake --name Hive_catalog --output table -i
    +---------------------------+
    |          Schema           |
    +---------------------------+
    | convert_example           |
    | default                   |
    | hive_best_performance     |
    | test_hive_convert_example |
    | test_multiple_data_source |
    +---------------------------+
    
    gcli schema details -m demo_metalake --name Hive_catalog.default --output 
table -i
    +---------+-----------------------+
    | Schema  |        Comment        |
    +---------+-----------------------+
    | default | Default Hive database |
    +---------+-----------------------+
    
    gcli table list -m demo_metalake --name Hive_catalog.default -i --output 
table
    +---------------------------------------+
    |                 Table                 |
    +---------------------------------------+
    | test_create_txt_tbl_without_delimited |
    | test_jinjia_tbl1                      |
    | test_jinjia_tbl2                      |
    | test                                  |
    | test1                                 |
    | example_table                         |
    | example_table2                        |
    +---------------------------------------+
    
    gcli table details -m demo_metalake --name Hive_catalog.default.test1  -i 
--output table
    
    +----------+---------+---------------+----------+---------+
    |   Name   |  Type   | AutoIncrement | Nullable | Comment |
    +----------+---------+---------------+----------+---------+
    | id       | integer | false         | true     | N/A     |
    | name     | string  | false         | true     | N/A     |
    | standard | long    | false         | true     | N/A     |
    | dt       | string  | false         | true     | N/A     |
    +----------+---------+---------------+----------+---------+
    ```
---
 .../org/apache/gravitino/cli/commands/Command.java |   2 +-
 .../apache/gravitino/cli/commands/ListSchema.java  |  32 ++++-
 .../apache/gravitino/cli/commands/ListTables.java  |  41 +++++--
 .../gravitino/cli/commands/SchemaDetails.java      |   2 +-
 .../gravitino/cli/commands/TableDetails.java       |   4 +-
 .../apache/gravitino/cli/outputs/TableFormat.java  | 103 ++++++++++++++++
 .../gravitino/cli/output/TestTableFormat.java      | 132 +++++++++++++++++++--
 7 files changed, 290 insertions(+), 26 deletions(-)

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 f789e2336b..de860f5a69 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 @@ public abstract class Command {
    * @param entity The entity to output.
    * @param <T> The type of entity.
    */
-  protected <T> void output(T entity) {
+  private <T> 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 bbc881ce06..05bfb49276 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 class ListSchema extends Command {
   @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 class ListSchema extends Command {
       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 e1389ed9d1..1f098efce7 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 class ListTables extends TableCommand {
   public void handle() {
     NameIdentifier[] tables = null;
     Namespace name = Namespace.of(schema);
+
     try {
       tables = tableCatalog().listTables(name);
     } catch (Exception exp) {
       exitWithError(exp.getMessage());
     }
 
-    List<String> 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 b9c530aea5..560fca20df 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 class SchemaDetails extends Command {
     }
 
     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 8ac9fb4fb6..23950e7ee9 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 class TableDetails extends TableCommand {
       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 6d08f73edf..e5021d62f3 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.List;
 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 abstract class TableFormat<T> extends 
BaseOutputFormat<T> {
       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 abstract class TableFormat<T> extends 
BaseOutputFormat<T> {
       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<Schema> {
+    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<Schema[]> {
+    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<Table> {
+    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<Table[]> {
+    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 64d5ea4987..60e9a50eea 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.io.PrintStream;
 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 @@ public class TestTableFormat {
   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 @@ public class TestTableFormat {
         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 @@ public class TestTableFormat {
         });
   }
 
-  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 @@ public class TestTableFormat {
 
     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;
+  }
 }

Reply via email to