blob: 88b8f32918632f002e0fe8e6da2979c0ce3fd5b4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.carbondata.core.metadata.datatype;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.carbondata.common.annotations.InterfaceAudience;
import org.apache.carbondata.common.annotations.InterfaceStability;
import org.apache.carbondata.core.metadata.schema.table.column.ColumnSchema;
/**
* A field represent one column
*/
@InterfaceAudience.User
@InterfaceStability.Unstable
public class Field {
private String name;
private DataType type;
private List<StructField> children;
private String parent;
private int schemaOrdinal = -1;
private int precision = 0;
private int scale = 0;
private String rawSchema = "";
private String columnComment = "";
/**
* Field Constructor
* @param name name of the field
* @param type datatype of field, specified in strings.
*/
public Field(String name, String type) {
this.name = name.toLowerCase().trim();
if (type.equalsIgnoreCase("string")) {
this.type = DataTypes.STRING;
} else if (type.equalsIgnoreCase("varchar")) {
this.type = DataTypes.VARCHAR;
} else if (type.equalsIgnoreCase("date")) {
this.type = DataTypes.DATE;
} else if (type.equalsIgnoreCase("timestamp")) {
this.type = DataTypes.TIMESTAMP;
} else if (type.equalsIgnoreCase("boolean")) {
this.type = DataTypes.BOOLEAN;
} else if (type.equalsIgnoreCase("byte")) {
this.type = DataTypes.BYTE;
} else if (type.equalsIgnoreCase("short")) {
this.type = DataTypes.SHORT;
} else if (type.equalsIgnoreCase("int")) {
this.type = DataTypes.INT;
} else if (type.equalsIgnoreCase("long")) {
this.type = DataTypes.LONG;
} else if (type.equalsIgnoreCase("float")) {
this.type = DataTypes.FLOAT;
} else if (type.equalsIgnoreCase("double")) {
this.type = DataTypes.DOUBLE;
} else if (type.equalsIgnoreCase("binary")) {
this.type = DataTypes.BINARY;
} else if (type.toLowerCase().startsWith("decimal")) {
if ("decimal".equalsIgnoreCase(type.toLowerCase())) {
this.type = DataTypes.createDefaultDecimalType();
} else {
try {
Matcher m = Pattern.compile("^decimal\\(([^)]+)\\)").matcher(type.toLowerCase());
m.find();
String matchedString = m.group(1);
String[] scaleAndPrecision = matchedString.split(",");
precision = Integer.parseInt(scaleAndPrecision[0].trim());
scale = Integer.parseInt(scaleAndPrecision[1].trim());
this.type = DataTypes.createDecimalType(precision, scale);
} catch (Exception e) {
throw new IllegalArgumentException("unsupported data type: " + type
+ ". Please use decimal or decimal(precision,scale), " +
"precision can be 10 and scale can be 2", e);
}
}
} else if (type.equalsIgnoreCase("array")) {
this.type = DataTypes.createDefaultArrayType();
} else if (type.equalsIgnoreCase("struct")) {
this.type = DataTypes.createDefaultStructType();
} else if (type.equalsIgnoreCase("map")) {
this.type = DataTypes.createDefaultMapType();
} else {
throw new IllegalArgumentException("unsupported data type: " + type);
}
}
public Field(String name, String type, List<StructField> fields) {
this.name = name.toLowerCase().trim();
this.children = fields;
if (type.equalsIgnoreCase("string")) {
this.type = DataTypes.STRING;
} else if (type.equalsIgnoreCase("varchar")) {
this.type = DataTypes.VARCHAR;
} else if (type.equalsIgnoreCase("date")) {
this.type = DataTypes.DATE;
} else if (type.equalsIgnoreCase("timestamp")) {
this.type = DataTypes.TIMESTAMP;
} else if (type.equalsIgnoreCase("boolean")) {
this.type = DataTypes.BOOLEAN;
} else if (type.equalsIgnoreCase("byte")) {
this.type = DataTypes.BYTE;
} else if (type.equalsIgnoreCase("short")) {
this.type = DataTypes.SHORT;
} else if (type.equalsIgnoreCase("int")) {
this.type = DataTypes.INT;
} else if (type.equalsIgnoreCase("long")) {
this.type = DataTypes.LONG;
} else if (type.equalsIgnoreCase("float")) {
this.type = DataTypes.FLOAT;
} else if (type.equalsIgnoreCase("double")) {
this.type = DataTypes.DOUBLE;
} else if (type.equalsIgnoreCase("binary")) {
this.type = DataTypes.BINARY;
} else if (type.equalsIgnoreCase("array")) {
this.type = DataTypes.createArrayType(fields.get(0).getDataType(),
fields.get(0).getFieldName());
} else if (type.equalsIgnoreCase("struct")) {
this.type = DataTypes.createStructType(fields);
} else {
throw new IllegalArgumentException("unsupported data type: " + type);
}
}
public Field(String name, DataType type, List<StructField> fields) {
this.name = name.toLowerCase().trim();
this.type = type;
this.children = fields;
}
public Field(String name, DataType type) {
this.name = name.toLowerCase().trim();
this.type = type;
initComplexTypeChildren();
}
/**
* Construct Field from ColumnSchema
*
* @param columnSchema ColumnSchema, Store the information about the column meta data
*/
public Field(ColumnSchema columnSchema) {
this.name = columnSchema.getColumnName().toLowerCase().trim();
this.type = columnSchema.getDataType();
children = new LinkedList<>();
schemaOrdinal = columnSchema.getSchemaOrdinal();
precision = columnSchema.getPrecision();
scale = columnSchema.getScale();
}
public String getFieldName() {
return name;
}
public DataType getDataType() {
return type;
}
public List<StructField> getChildren() {
return children;
}
public void setChildren(List<StructField> children) {
this.children = children;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public int getSchemaOrdinal() {
return schemaOrdinal;
}
public void setSchemaOrdinal(int schemaOrdinal) {
this.schemaOrdinal = schemaOrdinal;
}
public int getPrecision() {
return precision;
}
public void setPrecision(int precision) {
this.precision = precision;
}
public int getScale() {
return scale;
}
public void setScale(int scale) {
this.scale = scale;
}
public String getRawSchema() {
return rawSchema;
}
public void setRawSchema(String rawSchema) {
this.rawSchema = rawSchema;
}
public String getColumnComment() {
return columnComment;
}
public void setColumnComment(String columnComment) {
this.columnComment = columnComment;
}
/* for SDK, change string type to varchar by default for parent columns */
public void updateDataTypeToVarchar() {
this.type = DataTypes.VARCHAR;
}
private void initComplexTypeChildren() {
if (getDataType().isComplexType()) {
StructField subFields = prepareSubFields(getFieldName(), getDataType());
if (DataTypes.isArrayType(getDataType()) || DataTypes.isMapType(getDataType())) {
children = subFields.getChildren();
} else if (DataTypes.isStructType(getDataType())) {
children = ((StructType) subFields.getDataType()).getFields();
}
}
}
@Override
public String toString() {
return "Field{" +
"name='" + name + '\'' +
", type=" + type +
", schemaOrdinal=" + schemaOrdinal +
'}';
}
/**
* prepare sub fields for complex types
*
* @param fieldName column name
* @param dataType data type of column or it's children
* @return
*/
private StructField prepareSubFields(String fieldName, DataType dataType) {
if (DataTypes.isArrayType(dataType)) {
List<StructField> arrayFields = new ArrayList<>();
StructField arrayField = prepareSubFields(fieldName, ((ArrayType) dataType).getElementType());
arrayFields.add(arrayField);
return new StructField(fieldName, DataTypes.createArrayType(arrayField.getDataType()),
arrayFields);
} else if (DataTypes.isStructType(dataType)) {
List<StructField> structFields = new ArrayList<>();
List<StructField> fields = ((StructType) dataType).getFields();
for (StructField field : fields) {
structFields.add(prepareSubFields(field.getFieldName(), field.getDataType()));
}
return new StructField(fieldName, DataTypes.createStructType(structFields), structFields);
} else if (DataTypes.isMapType(dataType)) {
// Internally Map<key, value> is stored as Array<struct<key, value>>. So the below method
// will convert a map type into similar field structure. The columnSchema will be formed
// as Map<Struct<key,value>>
List<StructField> mapFields = new ArrayList<>();
MapType mapType = (MapType) dataType;
// key is primitive type so type can be fetched directly
StructField keyField = new StructField(fieldName + ".key", mapType.getKeyType());
StructField valueField = prepareSubFields(fieldName + ".value", mapType.getValueType());
mapFields.add(keyField);
mapFields.add(valueField);
StructField field =
new StructField(fieldName + ".val", DataTypes.createStructType(mapFields), mapFields);
MapType mapDataType = DataTypes.createMapType(keyField.getDataType(), field.getDataType());
List<StructField> mapStructField = new ArrayList<>();
mapStructField.add(field);
return new StructField(fieldName, mapDataType, mapStructField);
} else {
return new StructField(fieldName, dataType);
}
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Field) {
Field field = (Field) obj;
return (this.getDataType().equals(field.getDataType()))
&& (this.getFieldName().equals(field.getFieldName()))
&& (this.getSchemaOrdinal() == (field.getSchemaOrdinal()));
} else {
return false;
}
}
}