001package gudusoft.gsqlparser.common.structured; 002 003import gudusoft.gsqlparser.EDataType; 004import gudusoft.gsqlparser.nodes.TColumnDefinition; 005import gudusoft.gsqlparser.nodes.TObjectName; 006import gudusoft.gsqlparser.nodes.TTypeName; 007 008import java.util.ArrayList; 009import java.util.List; 010 011/** 012 * Reads the type a column's DDL declares into a {@link DeclaredTypeNode} tree. The grammars keep a nested type in 013 * different members of {@link TTypeName}; this is the one place that knows them (plan section 4.2): 014 * <ul> 015 * <li>STRUCT / ROW fields: {@link TTypeName#getColumnDefList()} (Hive, Impala, Spark SQL, Databricks 016 * {@code STRUCT<..>}, BigQuery, Athena, Presto/Trino {@code ROW(..)});</li> 017 * <li>ARRAY element: {@link TTypeName#getTypeOfList()} (Hive {@code listType_t}, BigQuery, the Trino family) or 018 * {@link TTypeName#getElementType()} (Databricks, Redshift);</li> 019 * <li>MAP key and value: {@link TTypeName#getKeyType()} / {@link TTypeName#getValueType()} (Databricks) or 020 * {@link TTypeName#getPrimitiveTypeOfMap()} / {@link TTypeName#getTypeOfMap()} (Hive, Impala, Redshift).</li> 021 * </ul> 022 * A part a grammar drops (Spark {@code ARRAY<T>}'s T, a Presto MAP's key and value) reads as null: the container is 023 * known, its parts are not. 024 */ 025public final class DeclaredTypeReader { 026 027 private DeclaredTypeReader() { 028 } 029 030 /** The declared type of {@code type}; null for a null type. */ 031 public static DeclaredTypeNode read(TTypeName type) { 032 return read(type, null); 033 } 034 035 private static DeclaredTypeNode read(TTypeName type, TObjectName fieldName) { 036 if (type == null) { 037 return null; 038 } 039 if (type.getColumnDefList() != null) { 040 List<DeclaredTypeNode> fields = new ArrayList<DeclaredTypeNode>(); 041 for (int i = 0; i < type.getColumnDefList().size(); i++) { 042 TColumnDefinition field = type.getColumnDefList().getColumn(i); 043 if (field.getColumnName() == null) { 044 // an unnamed field (BigQuery STRUCT<INT64, STRING>) cannot be read by name: no node 045 continue; 046 } 047 DeclaredTypeNode node = read(field.getDatatype(), field.getColumnName()); 048 fields.add(node != null ? node : new DeclaredTypeNode(DeclaredTypeNode.Kind.SCALAR, 049 field.getColumnName(), null, null, null, field.getDatatype())); 050 } 051 return new DeclaredTypeNode(DeclaredTypeNode.Kind.STRUCT, fieldName, fields, null, null, type); 052 } 053 EDataType dataType = type.getDataType(); 054 if (dataType == EDataType.array_t || dataType == EDataType.listType_t) { 055 TTypeName element = type.getTypeOfList() != null ? type.getTypeOfList() : type.getElementType(); 056 return new DeclaredTypeNode(DeclaredTypeNode.Kind.ARRAY, fieldName, null, read(element, null), null, type); 057 } 058 if (dataType == EDataType.map_t || dataType == EDataType.mapType_t) { 059 TTypeName key = type.getKeyType() != null ? type.getKeyType() : type.getPrimitiveTypeOfMap(); 060 TTypeName value = type.getValueType() != null ? type.getValueType() : type.getTypeOfMap(); 061 return new DeclaredTypeNode(DeclaredTypeNode.Kind.MAP, fieldName, null, read(value, null), 062 read(key, null), type); 063 } 064 if (dataType == EDataType.struct_t || dataType == EDataType.structType_t || dataType == EDataType.row_t) { 065 // a STRUCT whose member list the grammar did not keep: a STRUCT of unknown fields 066 return new DeclaredTypeNode(DeclaredTypeNode.Kind.STRUCT, fieldName, null, null, null, type); 067 } 068 return new DeclaredTypeNode(DeclaredTypeNode.Kind.SCALAR, fieldName, null, null, null, type); 069 } 070}