diff --git ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/show/ShowCreateTableOperation.java ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/show/ShowCreateTableOperation.java index affed03fbb..4b6c0f588e 100644 --- ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/show/ShowCreateTableOperation.java +++ ql/src/java/org/apache/hadoop/hive/ql/ddl/table/create/show/ShowCreateTableOperation.java @@ -54,6 +54,9 @@ import org.apache.hive.common.util.HiveStringUtils; import org.stringtemplate.v4.ST; +import com.google.common.base.CharMatcher; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -143,7 +146,7 @@ private String getExternal(Table table) { private String getColumns(Table table) { List columnDescs = new ArrayList(); for (FieldSchema col : table.getCols()) { - String columnDesc = " `" + col.getName() + "` " + col.getType(); + String columnDesc = " `" + col.getName() + "` " + formatType(col.getType()); if (col.getComment() != null) { columnDesc += " COMMENT '" + HiveStringUtils.escapeHiveCommand(col.getComment()) + "'"; } @@ -152,6 +155,72 @@ private String getColumns(Table table) { return StringUtils.join(columnDescs, ", \n"); } + private static final String STUCT_HEADER = "struct<"; + private static final String ARRAY_HEADER = "array<"; + private static final String MAP_HEADER = "map<"; + private static final String UNION_HEADER = "uniontype<"; + + /** Struct fields are identifiers, need to be put between ``. */ + private String formatType(String type) { + type = type.trim(); + if (type.startsWith(STUCT_HEADER)) { + StringBuilder formattedType = new StringBuilder(); + + Iterable structElements = splitTypeString(type.substring(STUCT_HEADER.length(), type.length() - 1), ','); + for (String structElement : structElements) { + int separator = structElement.indexOf(":"); + String name = structElement.substring(0, separator); + String elementType = formatType(structElement.substring(separator + 1)); + + if (formattedType.length() != 0) { + formattedType.append(", "); + } + formattedType.append("`" + name + "`:" + elementType); + } + + return STUCT_HEADER + formattedType + ">"; + } else if (type.startsWith(ARRAY_HEADER)) { + String elementType = formatType(type.substring(ARRAY_HEADER.length(), type.length() - 1)); + return ARRAY_HEADER + elementType + ">"; + } else if (type.startsWith(MAP_HEADER)) { + int separator = type.indexOf(","); + String keyType = type.substring(MAP_HEADER.length(), separator); + String valueType = formatType(type.substring(separator + 1, type.length() - 1)); + return MAP_HEADER + keyType + ", " + valueType + ">"; + } else if (type.startsWith(UNION_HEADER)) { + StringBuilder formattedType = new StringBuilder(); + + Iterable unionTypes = splitTypeString(type.substring(UNION_HEADER.length(), type.length() - 1), ','); + for (String unionType : unionTypes) { + if (formattedType.length() != 0) { + formattedType.append(", "); + } + formattedType.append(formatType(unionType)); + } + return UNION_HEADER + formattedType.toString() + ">"; + } else { + return type; + } + } + + private Iterable splitTypeString(String typeString, char splitChar) { + Splitter splitter = Splitter.on(CharMatcher.forPredicate(new Predicate() { + private int count = 0; + @Override + public boolean apply(Character c) { + if (c == '<') { + count++; + } + if (c == '>') { + count--; + } + return c == splitChar && count == 0; + } + })); + + return splitter.split(typeString); + } + private String getComment(Table table) { String comment = table.getProperty("comment"); return (comment != null) ? "COMMENT '" + HiveStringUtils.escapeHiveCommand(comment) + "'" : ""; diff --git ql/src/test/queries/clientpositive/show_create_table_db_table.q ql/src/test/queries/clientpositive/show_create_table_db_table.q index 35ac5bf73e..ffe36180ca 100644 --- ql/src/test/queries/clientpositive/show_create_table_db_table.q +++ ql/src/test/queries/clientpositive/show_create_table_db_table.q @@ -5,12 +5,15 @@ SHOW DATABASES; CREATE TABLE tmp_feng.tmp_showcrt1(key string, value int); CREATE TABLE tmp_feng.tmp_showcrt2(key string, value int) skewed by (key) on ('1','2'); CREATE TABLE tmp_feng.tmp_showcrt3(key string, value int) skewed by (key) on ('1','2') stored as directories; +CREATE TABLE tmp_feng.tmp_showcrt4(s1 struct, s2 struct, array>>>>>); USE default; SHOW CREATE TABLE tmp_feng.tmp_showcrt1; SHOW CREATE TABLE tmp_feng.tmp_showcrt2; SHOW CREATE TABLE tmp_feng.tmp_showcrt3; +SHOW CREATE TABLE tmp_feng.tmp_showcrt4; DROP TABLE tmp_feng.tmp_showcrt1; DROP TABLE tmp_feng.tmp_showcrt2; DROP TABLE tmp_feng.tmp_showcrt3; +DROP TABLE tmp_feng.tmp_showcrt4; DROP DATABASE tmp_feng; diff --git ql/src/test/results/clientpositive/show_create_table_db_table.q.out ql/src/test/results/clientpositive/show_create_table_db_table.q.out index 33410c8582..eeb353d52d 100644 --- ql/src/test/results/clientpositive/show_create_table_db_table.q.out +++ ql/src/test/results/clientpositive/show_create_table_db_table.q.out @@ -34,6 +34,14 @@ POSTHOOK: query: CREATE TABLE tmp_feng.tmp_showcrt3(key string, value int) skewe POSTHOOK: type: CREATETABLE POSTHOOK: Output: database:tmp_feng POSTHOOK: Output: tmp_feng@tmp_showcrt3 +PREHOOK: query: CREATE TABLE tmp_feng.tmp_showcrt4(s1 struct, s2 struct, array>>>>>) +PREHOOK: type: CREATETABLE +PREHOOK: Output: database:tmp_feng +PREHOOK: Output: tmp_feng@tmp_showcrt4 +POSTHOOK: query: CREATE TABLE tmp_feng.tmp_showcrt4(s1 struct, s2 struct, array>>>>>) +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: database:tmp_feng +POSTHOOK: Output: tmp_feng@tmp_showcrt4 PREHOOK: query: USE default PREHOOK: type: SWITCHDATABASE PREHOOK: Input: database:default @@ -105,6 +113,26 @@ LOCATION TBLPROPERTIES ( 'bucketing_version'='2', #### A masked pattern was here #### +PREHOOK: query: SHOW CREATE TABLE tmp_feng.tmp_showcrt4 +PREHOOK: type: SHOW_CREATETABLE +PREHOOK: Input: tmp_feng@tmp_showcrt4 +POSTHOOK: query: SHOW CREATE TABLE tmp_feng.tmp_showcrt4 +POSTHOOK: type: SHOW_CREATETABLE +POSTHOOK: Input: tmp_feng@tmp_showcrt4 +CREATE TABLE `tmp_feng.tmp_showcrt4`( + `s1` struct<`p1`:string>, + `s2` struct<`p2`:array, array>>>>>) +ROW FORMAT SERDE + 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' +STORED AS INPUTFORMAT + 'org.apache.hadoop.mapred.TextInputFormat' +OUTPUTFORMAT + 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' +LOCATION +#### A masked pattern was here #### +TBLPROPERTIES ( + 'bucketing_version'='2', +#### A masked pattern was here #### PREHOOK: query: DROP TABLE tmp_feng.tmp_showcrt1 PREHOOK: type: DROPTABLE PREHOOK: Input: tmp_feng@tmp_showcrt1 @@ -129,6 +157,14 @@ POSTHOOK: query: DROP TABLE tmp_feng.tmp_showcrt3 POSTHOOK: type: DROPTABLE POSTHOOK: Input: tmp_feng@tmp_showcrt3 POSTHOOK: Output: tmp_feng@tmp_showcrt3 +PREHOOK: query: DROP TABLE tmp_feng.tmp_showcrt4 +PREHOOK: type: DROPTABLE +PREHOOK: Input: tmp_feng@tmp_showcrt4 +PREHOOK: Output: tmp_feng@tmp_showcrt4 +POSTHOOK: query: DROP TABLE tmp_feng.tmp_showcrt4 +POSTHOOK: type: DROPTABLE +POSTHOOK: Input: tmp_feng@tmp_showcrt4 +POSTHOOK: Output: tmp_feng@tmp_showcrt4 PREHOOK: query: DROP DATABASE tmp_feng PREHOOK: type: DROPDATABASE PREHOOK: Input: database:tmp_feng