11package com .altinity .ice .internal .cmd ;
22
3- import com .fasterxml . jackson . core . JsonParser ;
4- import com .fasterxml . jackson . databind . JsonNode ;
3+ import com .altinity . ice . internal . model . TableMetadata ;
4+ import com .altinity . ice . internal . model . TableMetadata .* ;
55import com .fasterxml .jackson .databind .ObjectMapper ;
66import com .fasterxml .jackson .dataformat .yaml .YAMLFactory ;
77import java .io .IOException ;
88import java .nio .ByteBuffer ;
99import java .time .Instant ;
1010import java .time .ZoneId ;
1111import java .time .format .DateTimeFormatter ;
12+ import java .util .ArrayList ;
1213import java .util .List ;
1314import java .util .Map ;
14- import java .util .stream .Collectors ;
1515import org .apache .iceberg .*;
1616import org .apache .iceberg .catalog .Namespace ;
1717import org .apache .iceberg .catalog .TableIdentifier ;
@@ -24,13 +24,10 @@ public final class Describe {
2424
2525 private Describe () {}
2626
27- // TODO: refactor: the use of StringBuilder below is absolutely criminal
28- public static void run (RESTCatalog catalog , String target , boolean json , boolean includeMetrics )
29- throws IOException {
27+ public static void run (RESTCatalog catalog , String target , boolean json ) throws IOException {
3028 String targetNamespace = null ;
3129 String targetTable = null ;
3230 if (target != null && !target .isEmpty ()) {
33- // TODO: support catalog.ns.table
3431 var s = target .split ("[.]" , 2 );
3532 switch (s .length ) {
3633 case 2 :
@@ -42,8 +39,8 @@ public static void run(RESTCatalog catalog, String target, boolean json, boolean
4239 break ;
4340 }
4441 }
45- // FIXME: there is no need to list nss/tables when target is given
46- var sb = new StringBuilder ();
42+
43+ List < TableMetadata > tablesMetadata = new ArrayList <> ();
4744 List <Namespace > namespaces = catalog .listNamespaces ();
4845 for (Namespace namespace : namespaces ) {
4946 if (targetNamespace != null && !targetNamespace .equals (namespace .toString ())) {
@@ -54,73 +51,55 @@ public static void run(RESTCatalog catalog, String target, boolean json, boolean
5451 if (targetTable != null && !targetTable .equals (tableId .name ())) {
5552 continue ;
5653 }
57- sb .append ("---\n " );
58- sb .append ("kind: Table\n " );
59- sb .append ("metadata:\n " );
60- sb .append ("\t id: " + tableId + "\n " );
6154 Table table = catalog .loadTable (tableId );
62- sb .append ("data:\n " );
63- sb .append ("\t schema_raw: |-\n " + prefixEachLine (table .schema ().toString (), "\t \t " ) + "\n " );
64- sb .append (
65- "\t partition_spec_raw: |-\n " + prefixEachLine (table .spec ().toString (), "\t \t " ) + "\n " );
66- sb .append (
67- "\t sort_order_raw: |-\n " + prefixEachLine (table .sortOrder ().toString (), "\t \t " ) + "\n " );
68- sb .append ("\t properties: \n " );
69- for (var property : table .properties ().entrySet ()) {
70- var v = property .getValue ();
71- if (v .contains ("\n " )) {
72- sb .append ("\t \t " + property .getKey () + ": |-\n " + prefixEachLine (v , "\t \t \t " ) + "\n " );
73- } else {
74- sb .append ("\t \t " + property .getKey () + ": \" " + v + "\" \n " );
75- }
76- }
77- sb .append ("\t location: " + table .location () + "\n " );
78- sb .append ("\t current_snapshot: \n " );
7955 Snapshot snapshot = table .currentSnapshot ();
56+
57+ SnapshotInfo snapshotInfo = null ;
8058 if (snapshot != null ) {
81- sb .append ("\t \t sequence_number: " + snapshot .sequenceNumber () + "\n " );
82- sb .append ("\t \t id: " + snapshot .snapshotId () + "\n " );
83- sb .append ("\t \t parent_id: " + snapshot .parentId () + "\n " );
84- sb .append ("\t \t timestamp: " + snapshot .timestampMillis () + "\n " );
85- sb .append (
86- "\t \t timestamp_iso: \" "
87- + Instant .ofEpochMilli (snapshot .timestampMillis ()).toString ()
88- + "\" \n " );
89- sb .append (
90- "\t \t timestamp_iso_local: \" "
91- + Instant .ofEpochMilli (snapshot .timestampMillis ())
59+ snapshotInfo =
60+ new SnapshotInfo (
61+ snapshot .sequenceNumber (),
62+ snapshot .snapshotId (),
63+ snapshot .parentId (),
64+ snapshot .timestampMillis (),
65+ Instant .ofEpochMilli (snapshot .timestampMillis ()).toString (),
66+ Instant .ofEpochMilli (snapshot .timestampMillis ())
9267 .atZone (ZoneId .systemDefault ())
93- .format (DateTimeFormatter .ISO_OFFSET_DATE_TIME )
94- + "\" \n " );
95- sb .append ("\t \t operation: " + snapshot .operation () + "\n " );
96- sb .append ("\t \t summary:\n " );
97- for (var property : snapshot .summary ().entrySet ()) {
98- sb .append ("\t \t \t " + property .getKey () + ": \" " + property .getValue () + "\" \n " );
99- }
100- sb .append ("\t \t location: " + snapshot .manifestListLocation () + "\n " );
68+ .format (DateTimeFormatter .ISO_OFFSET_DATE_TIME ),
69+ snapshot .operation (),
70+ snapshot .summary (),
71+ snapshot .manifestListLocation ());
10172 }
10273
103- if (includeMetrics ) {
104- printTableMetrics (table , sb );
105- }
74+ List <MetricsInfo > metrics = getTableMetrics (table );
75+
76+ TableData tableData =
77+ new TableData (
78+ table .schema ().toString (),
79+ table .spec ().toString (),
80+ table .sortOrder ().toString (),
81+ table .properties (),
82+ table .location (),
83+ snapshotInfo ,
84+ metrics );
85+
86+ tablesMetadata .add (new TableMetadata ("Table" , new Metadata (tableId .toString ()), tableData ));
10687 }
10788 }
108- String r = sb .toString ().replace ("\t " , " " );
109- if (json ) {
110- r = convertYamlToJson (r );
111- }
112- System .out .println (r );
89+
90+ ObjectMapper mapper = json ? new ObjectMapper () : new ObjectMapper (new YAMLFactory ());
91+ String output = mapper .writeValueAsString (tablesMetadata );
92+ System .out .println (output );
11393 }
11494
115- private static void printTableMetrics (Table table , StringBuilder buffer ) throws IOException {
95+ private static List <MetricsInfo > getTableMetrics (Table table ) throws IOException {
96+ List <MetricsInfo > metricsList = new ArrayList <>();
11697 TableScan scan = table .newScan ().includeColumnStats ();
11798 CloseableIterable <FileScanTask > tasks = scan .planFiles ();
11899
119100 for (FileScanTask task : tasks ) {
120101 DataFile dataFile = task .file ();
121- buffer .append ("\t metrics:\n " );
122- buffer .append ("\t \t file: " + dataFile .path () + "\n " );
123- buffer .append ("\t \t record_count: " + dataFile .recordCount () + "\n " );
102+ List <ColumnMetrics > columnMetrics = new ArrayList <>();
124103
125104 Map <Integer , Long > valueCounts = dataFile .valueCounts ();
126105 Map <Integer , Long > nullCounts = dataFile .nullValueCounts ();
@@ -131,52 +110,36 @@ private static void printTableMetrics(Table table, StringBuilder buffer) throws
131110 continue ;
132111 }
133112
134- buffer .append ("\t \t columns:\n " );
135113 for (Types .NestedField field : table .schema ().columns ()) {
136114 int id = field .fieldId ();
137- buffer .append ("\t \t \t " + field .name () + ":\n " );
138- if (valueCounts != null ) {
139- buffer .append ("\t \t \t \t value_count: " + valueCounts .get (id ) + "\n " );
140- }
141- if (nullCounts != null ) {
142- buffer .append ("\t \t \t \t null_count: " + nullCounts .get (id ) + "\n " );
143- }
115+ String lowerBound = null ;
116+ String upperBound = null ;
117+
144118 if (lowerBounds != null ) {
145119 ByteBuffer lower = lowerBounds .get (id );
146- String lowerStr =
147- lower != null ? Conversions .fromByteBuffer (field .type (), lower ).toString () : "null" ;
148- buffer .append ("\t \t \t \t lower_bound: " + lowerStr + "\n " );
120+ lowerBound =
121+ lower != null ? Conversions .fromByteBuffer (field .type (), lower ).toString () : null ;
149122 }
150123 if (upperBounds != null ) {
151124 ByteBuffer upper = upperBounds .get (id );
152- String upperStr =
153- upper != null ? Conversions .fromByteBuffer (field .type (), upper ).toString () : "null" ;
154- buffer .append ("\t \t \t \t upper_bound: " + upperStr + "\n " );
125+ upperBound =
126+ upper != null ? Conversions .fromByteBuffer (field .type (), upper ).toString () : null ;
155127 }
156- }
157- }
158-
159- tasks .close ();
160- }
161128
162- private static String convertYamlToJson (String yaml ) throws IOException {
163- YAMLFactory yamlFactory = new YAMLFactory ();
164- ObjectMapper yamlReader = new ObjectMapper (yamlFactory );
165- ObjectMapper jsonWriter = new ObjectMapper ();
166- StringBuilder result = new StringBuilder ();
167- try (JsonParser parser = yamlFactory .createParser (yaml )) {
168- while (!parser .isClosed ()) {
169- JsonNode node = yamlReader .readTree (parser );
170- if (node != null ) {
171- String json = jsonWriter .writeValueAsString (node );
172- result .append (json ).append ("\n " );
173- }
129+ columnMetrics .add (
130+ new ColumnMetrics (
131+ field .name (),
132+ valueCounts != null ? valueCounts .get (id ) : null ,
133+ nullCounts != null ? nullCounts .get (id ) : null ,
134+ lowerBound ,
135+ upperBound ));
174136 }
137+
138+ metricsList .add (
139+ new MetricsInfo (dataFile .path ().toString (), dataFile .recordCount (), columnMetrics ));
175140 }
176- return result .toString ().trim ();
177- }
178141
179- private static String prefixEachLine ( String v , String prefix ) {
180- return v . lines (). map ( line -> prefix + line ). collect ( Collectors . joining ( " \n " )) ;
142+ tasks . close ();
143+ return metricsList ;
181144 }
182145}
0 commit comments