Skip to content

Commit

Permalink
feat: add SHOW TYPES to list all custom types (#3280)
Browse files Browse the repository at this point in the history
  • Loading branch information
agavra committed Sep 4, 2019
1 parent b5bc9ff commit 13fde33
Show file tree
Hide file tree
Showing 15 changed files with 402 additions and 33 deletions.
38 changes: 7 additions & 31 deletions ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.confluent.ksql.cli.console.table.builder.TableBuilder;
import io.confluent.ksql.cli.console.table.builder.TablesListTableBuilder;
import io.confluent.ksql.cli.console.table.builder.TopicDescriptionTableBuilder;
import io.confluent.ksql.cli.console.table.builder.TypeListTableBuilder;
import io.confluent.ksql.json.JsonMapper;
import io.confluent.ksql.rest.entity.ArgumentInfo;
import io.confluent.ksql.rest.entity.CommandStatusEntity;
Expand All @@ -66,15 +67,14 @@
import io.confluent.ksql.rest.entity.QueryDescriptionEntity;
import io.confluent.ksql.rest.entity.QueryDescriptionList;
import io.confluent.ksql.rest.entity.RunningQuery;
import io.confluent.ksql.rest.entity.SchemaInfo;
import io.confluent.ksql.rest.entity.SourceDescription;
import io.confluent.ksql.rest.entity.SourceDescriptionEntity;
import io.confluent.ksql.rest.entity.SourceDescriptionList;
import io.confluent.ksql.rest.entity.StreamedRow;
import io.confluent.ksql.rest.entity.StreamsList;
import io.confluent.ksql.rest.entity.TablesList;
import io.confluent.ksql.rest.entity.TopicDescription;
import io.confluent.ksql.schema.ksql.SqlBaseType;
import io.confluent.ksql.rest.entity.TypeList;
import io.confluent.ksql.util.CmdLineUtil;
import io.confluent.ksql.util.HandlerMaps;
import io.confluent.ksql.util.HandlerMaps.ClassHandlerMap1;
Expand Down Expand Up @@ -157,6 +157,8 @@ public class Console implements Closeable {
tablePrinter(ConnectorList.class, ConnectorListTableBuilder::new))
.put(ConnectorDescription.class,
Console::printConnectorDescription)
.put(TypeList.class,
tablePrinter(TypeList.class, TypeListTableBuilder::new))
.put(ErrorEntity.class,
tablePrinter(ErrorEntity.class, ErrorEntityTableBuilder::new))
.build();
Expand Down Expand Up @@ -440,39 +442,13 @@ private void printWarnings(final KsqlEntity entity) {
}
}

@SuppressWarnings("ConstantConditions")
private static String schemaToTypeString(final SchemaInfo schema) {
switch (schema.getType()) {
case ARRAY:
return SqlBaseType.ARRAY + "<"
+ schemaToTypeString(schema.getMemberSchema().get())
+ ">";
case MAP:
return SqlBaseType.MAP
+ "<"
+ SqlBaseType.STRING + ", "
+ schemaToTypeString(schema.getMemberSchema().get())
+ ">";
case STRUCT:
return schema.getFields().get()
.stream()
.map(f -> f.getName() + " " + schemaToTypeString(f.getSchema()))
.collect(Collectors.joining(", ", SqlBaseType.STRUCT + "<", ">"));
case STRING:
return "VARCHAR(STRING)";
default:
return schema.getType().name();
}
}

private static String formatFieldType(final FieldInfo field, final String keyField) {

if (field.getName().equals("ROWTIME") || field.getName().equals("ROWKEY")) {
return String.format("%-16s %s", schemaToTypeString(field.getSchema()), "(system)");
return String.format("%-16s %s", field.getSchema().toTypeString(), "(system)");
} else if (keyField != null && keyField.contains("." + field.getName())) {
return String.format("%-16s %s", schemaToTypeString(field.getSchema()), "(key)");
return String.format("%-16s %s", field.getSchema().toTypeString(), "(key)");
} else {
return schemaToTypeString(field.getSchema());
return field.getSchema().toTypeString();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 Confluent Inc.
*
* Licensed under the Confluent Community License (the "License"; you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.confluent.io/confluent-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package io.confluent.ksql.cli.console.table.builder;

import com.google.common.collect.ImmutableList;
import io.confluent.ksql.cli.console.table.Table;
import io.confluent.ksql.rest.entity.TypeList;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;

public class TypeListTableBuilder implements TableBuilder<TypeList> {

private static final List<String> HEADERS = ImmutableList.of("Type Name", "Schema");

@Override
public Table buildTable(final TypeList entity) {
return new Table.Builder()
.withColumnHeaders(HEADERS)
.withRows(entity
.getTypes()
.entrySet()
.stream()
.sorted(Comparator.comparing(Entry::getKey))
.map(entry -> ImmutableList.of(entry.getKey(), entry.getValue().toTypeString())))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import io.confluent.ksql.rest.entity.StreamsList;
import io.confluent.ksql.rest.entity.TablesList;
import io.confluent.ksql.rest.entity.TopicDescription;
import io.confluent.ksql.rest.entity.TypeList;
import io.confluent.ksql.rest.server.computation.CommandId;
import io.confluent.ksql.rest.util.EntityUtil;
import io.confluent.ksql.schema.ksql.LogicalSchema;
Expand Down Expand Up @@ -863,6 +864,67 @@ public void shouldPrintConnectorsList() throws IOException {
}
}

@Test
public void shouldPrintTypesList() throws IOException {
// Given:
final KsqlEntityList entities = new KsqlEntityList(ImmutableList.of(
new TypeList("statement", ImmutableMap.of(
"typeB", new SchemaInfo(
SqlBaseType.ARRAY,
null,
new SchemaInfo(SqlBaseType.STRING, null, null)),
"typeA", new SchemaInfo(
SqlBaseType.STRUCT,
ImmutableList.of(
new FieldInfo("f1", new SchemaInfo(SqlBaseType.STRING, null, null))),
null)
))
));

// When:
console.printKsqlEntityList(entities);

// Then:
final String output = terminal.getOutputString();
if (console.getOutputFormat() == OutputFormat.JSON) {
assertThat(output, is("[ {\n"
+ " \"@type\" : \"type_list\",\n"
+ " \"statementText\" : \"statement\",\n"
+ " \"types\" : {\n"
+ " \"typeB\" : {\n"
+ " \"type\" : \"ARRAY\",\n"
+ " \"fields\" : null,\n"
+ " \"memberSchema\" : {\n"
+ " \"type\" : \"STRING\",\n"
+ " \"fields\" : null,\n"
+ " \"memberSchema\" : null\n"
+ " }\n"
+ " },\n"
+ " \"typeA\" : {\n"
+ " \"type\" : \"STRUCT\",\n"
+ " \"fields\" : [ {\n"
+ " \"name\" : \"f1\",\n"
+ " \"schema\" : {\n"
+ " \"type\" : \"STRING\",\n"
+ " \"fields\" : null,\n"
+ " \"memberSchema\" : null\n"
+ " }\n"
+ " } ],\n"
+ " \"memberSchema\" : null\n"
+ " }\n"
+ " },\n"
+ " \"warnings\" : [ ]\n"
+ "} ]\n"));
} else {
assertThat(output, is("\n"
+ " Type Name | Schema \n"
+ "----------------------------------------\n"
+ " typeA | STRUCT<f1 VARCHAR(STRING)> \n"
+ " typeB | ARRAY<VARCHAR(STRING)> \n"
+ "----------------------------------------\n"));
}
}

@Test
public void testPrintExecuptionPlan() throws IOException {
// Given:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ statement
| (LIST | SHOW) TABLES EXTENDED? #listTables
| (LIST | SHOW) FUNCTIONS #listFunctions
| (LIST | SHOW) (SOURCE | SINK)? CONNECTORS #listConnectors
| (LIST | SHOW) TYPES #listTypes
| DESCRIBE EXTENDED? qualifiedName #showColumns
| DESCRIBE FUNCTION qualifiedName #describeFunction
| DESCRIBE CONNECTOR identifier #describeConnector
Expand Down Expand Up @@ -319,7 +320,7 @@ nonReserved
| STRUCT | MAP | ARRAY | PARTITION
| INTEGER | DATE | TIME | TIMESTAMP | INTERVAL | ZONE
| YEAR | MONTH | DAY | HOUR | MINUTE | SECOND
| EXPLAIN | ANALYZE | TYPE
| EXPLAIN | ANALYZE | TYPE | TYPES
| SET | RESET
| IF
| SOURCE | SINK
Expand Down Expand Up @@ -403,6 +404,7 @@ PRINT: 'PRINT';
EXPLAIN: 'EXPLAIN';
ANALYZE: 'ANALYZE';
TYPE: 'TYPE';
TYPES: 'TYPES';
CAST: 'CAST';
SHOW: 'SHOW';
LIST: 'LIST';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import io.confluent.ksql.parser.SqlBaseParser.IntervalClauseContext;
import io.confluent.ksql.parser.SqlBaseParser.LimitClauseContext;
import io.confluent.ksql.parser.SqlBaseParser.ListConnectorsContext;
import io.confluent.ksql.parser.SqlBaseParser.ListTypesContext;
import io.confluent.ksql.parser.SqlBaseParser.RegisterTypeContext;
import io.confluent.ksql.parser.SqlBaseParser.SingleStatementContext;
import io.confluent.ksql.parser.SqlBaseParser.TablePropertiesContext;
Expand Down Expand Up @@ -99,6 +100,7 @@
import io.confluent.ksql.parser.tree.ListStreams;
import io.confluent.ksql.parser.tree.ListTables;
import io.confluent.ksql.parser.tree.ListTopics;
import io.confluent.ksql.parser.tree.ListTypes;
import io.confluent.ksql.parser.tree.PrintTopic;
import io.confluent.ksql.parser.tree.Query;
import io.confluent.ksql.parser.tree.RegisterType;
Expand Down Expand Up @@ -623,6 +625,11 @@ public Node visitDropType(final DropTypeContext ctx) {
return new DropType(getLocation(ctx), getIdentifierText(ctx.identifier()));
}

@Override
public Node visitListTypes(final ListTypesContext ctx) {
return new ListTypes(getLocation(ctx));
}

@Override
public Node visitTerminateQuery(final SqlBaseParser.TerminateQueryContext context) {
return new TerminateQuery(getLocation(context), context.qualifiedName().getText());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ protected R visitListTables(final ListTables node, final C context) {
return visitStatement(node, context);
}

protected R visitListTypes(final ListTypes listTypes, final C context) {
return visitStatement(listTypes, context);
}

protected R visitUnsetProperty(final UnsetProperty node, final C context) {
return visitStatement(node, context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2019 Confluent Inc.
*
* Licensed under the Confluent Community License (the "License"; you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.confluent.io/confluent-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package io.confluent.ksql.parser.tree;

import com.google.errorprone.annotations.Immutable;
import io.confluent.ksql.parser.NodeLocation;
import java.util.Objects;
import java.util.Optional;

@Immutable
public class ListTypes extends Statement {

public ListTypes(final Optional<NodeLocation> location) {
super(location);
}

@Override
public <R, C> R accept(final AstVisitor<R, C> visitor, final C context) {
return visitor.visitListTypes(this, context);
}

@Override
public int hashCode() {
return Objects.hashCode(getClass());
}

@Override
public boolean equals(final Object obj) {
return this == obj || (obj != null && obj.getClass().equals(getClass()));
}

@Override
public String toString() {
return "ListTypes{}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
@JsonSubTypes.Type(value = DropConnectorEntity.class, name = "drop_connector"),
@JsonSubTypes.Type(value = ConnectorList.class, name = "connector_list"),
@JsonSubTypes.Type(value = ConnectorDescription.class, name = "connector_description"),
@JsonSubTypes.Type(value = TypeList.class, name = "type_list"),
@JsonSubTypes.Type(value = ErrorEntity.class, name = "error_entity")
})
public abstract class KsqlEntity {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.Immutable;
import io.confluent.ksql.schema.ksql.SqlBaseType;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

@Immutable
@JsonIgnoreProperties(ignoreUnknown = true)
Expand Down Expand Up @@ -75,4 +79,30 @@ public boolean equals(final Object other) {
public int hashCode() {
return Objects.hash(type, fields, memberSchema);
}

private static final Map<SqlBaseType, Function<SchemaInfo, String>> TO_TYPE_STRING =
ImmutableMap.<SqlBaseType, Function<SchemaInfo, String>>builder()
.put(SqlBaseType.STRING, si -> "VARCHAR(STRING)")
.put(
SqlBaseType.ARRAY,
si -> SqlBaseType.ARRAY + "<" + si.memberSchema.toTypeString() + ">")
.put(
SqlBaseType.MAP,
si -> SqlBaseType.MAP
+ "<" + SqlBaseType.STRING
+ ", " + si.memberSchema.toTypeString()
+ ">")
.put(
SqlBaseType.STRUCT,
si -> si.fields
.stream()
.map(f -> f.getName() + " " + f.getSchema().toTypeString())
.collect(Collectors.joining(", ", SqlBaseType.STRUCT + "<", ">")))
.build();

public String toTypeString() {
// needs a map instead of switch because for some reason switch creates an
// internal class with no annotations that messes up EntityTest
return TO_TYPE_STRING.getOrDefault(type, si -> si.type.name()).apply(this);
}
}
Loading

0 comments on commit 13fde33

Please sign in to comment.