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 <[email protected]>
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;
+ }
}