| /* |
| * 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.schema.table.column; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataInput; |
| import java.io.DataInputStream; |
| import java.io.DataOutput; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.carbondata.core.metadata.datatype.DataType; |
| import org.apache.carbondata.core.metadata.datatype.DataTypes; |
| import org.apache.carbondata.core.metadata.datatype.DecimalType; |
| import org.apache.carbondata.core.metadata.encoder.Encoding; |
| import org.apache.carbondata.core.metadata.schema.table.Writable; |
| import org.apache.carbondata.core.metadata.schema.table.WritableUtil; |
| import org.apache.carbondata.core.preagg.TimeSeriesUDF; |
| |
| /** |
| * Store the information about the column meta data present the table |
| */ |
| public class ColumnSchema implements Serializable, Writable, Cloneable { |
| |
| /** |
| * serialization version |
| */ |
| private static final long serialVersionUID = 7676766554874863763L; |
| |
| /** |
| * dataType |
| */ |
| private DataType dataType; |
| |
| /** |
| * Name of the column. If it is a complex data type, we follow a naming rule |
| * grand_parent_column.parent_column.child_column |
| * For Array types, two columns will be stored one for |
| * the array type and one for the primitive type with |
| * the name parent_column.value |
| */ |
| private String columnName; |
| |
| /** |
| * Unique ID for a column. if this is dimension, |
| * it is an unique ID that used in dictionary |
| */ |
| private String columnUniqueId; |
| |
| /** |
| * column reference id |
| */ |
| private String columnReferenceId; |
| |
| /** |
| * whether it is stored as columnar format or row format |
| */ |
| private boolean isColumnar = true; |
| |
| /** |
| * List of encoding that are chained to apply the data for this column |
| */ |
| private List<Encoding> encodingList; |
| |
| /** |
| * Whether the column is a dimension or measure |
| */ |
| private boolean isDimensionColumn; |
| |
| /** |
| * Used when this column contains decimal data. |
| */ |
| private int scale; |
| |
| private int precision; |
| |
| private int schemaOrdinal; |
| /** |
| * Nested fields. Since thrift does not support nested fields, |
| * the nesting is flattened to a single list by a depth-first traversal. |
| * The children count is used to construct the nested relationship. |
| * This field is not set when the element is a primitive type |
| */ |
| private int numberOfChild; |
| |
| /** |
| * used in case of schema restructuring |
| */ |
| private byte[] defaultValue; |
| |
| /** |
| * Column properties |
| */ |
| private Map<String, String> columnProperties; |
| |
| /** |
| * used to define the column visibility of column default is false |
| */ |
| private boolean invisible = false; |
| |
| private boolean isSortColumn = false; |
| |
| /** |
| * Whether it is a spatial index column |
| */ |
| private boolean spatialColumn = false; |
| |
| /** |
| * aggregate function used in pre aggregate table |
| */ |
| private String aggFunction = ""; |
| |
| /** |
| * list of parent column relations |
| */ |
| private List<ParentColumnTableRelation> parentColumnTableRelations; |
| |
| /** |
| * time-series function applied on column |
| */ |
| private String timeSeriesFunction = ""; |
| |
| /** |
| * set whether the column is local dictionary column or not. |
| */ |
| private boolean isLocalDictColumn = false; |
| |
| /** |
| * @return isLocalDictColumn |
| */ |
| public boolean isLocalDictColumn() { |
| return isLocalDictColumn; |
| } |
| |
| /** |
| * @param localDictColumn whether column is local dictionary column |
| */ |
| public void setLocalDictColumn(boolean localDictColumn) { |
| isLocalDictColumn = localDictColumn; |
| } |
| |
| /** |
| * @return the columnName |
| */ |
| public String getColumnName() { |
| return columnName; |
| } |
| |
| /** |
| * @param columnName the columnName to set |
| */ |
| public void setColumnName(String columnName) { |
| this.columnName = columnName; |
| } |
| |
| /** |
| * @return the columnUniqueId |
| */ |
| public String getColumnUniqueId() { |
| return columnUniqueId; |
| } |
| |
| /** |
| * @param columnUniqueId the columnUniqueId to set |
| */ |
| public void setColumnUniqueId(String columnUniqueId) { |
| this.columnUniqueId = columnUniqueId; |
| } |
| |
| /** |
| * @return the isDimensionColumn |
| */ |
| public boolean isDimensionColumn() { |
| return isDimensionColumn; |
| } |
| |
| /** |
| * @param isDimensionColumn the isDimensionColumn to set |
| */ |
| public void setDimensionColumn(boolean isDimensionColumn) { |
| this.isDimensionColumn = isDimensionColumn; |
| } |
| |
| /** |
| * the isUseInvertedIndex |
| */ |
| public boolean isUseInvertedIndex() { |
| return this.hasEncoding(Encoding.INVERTED_INDEX); |
| } |
| |
| /** |
| * @param useInvertedIndex the useInvertedIndex to set |
| */ |
| public void setUseInvertedIndex(boolean useInvertedIndex) { |
| if (useInvertedIndex) { |
| if (!hasEncoding(Encoding.INVERTED_INDEX)) { |
| this.getEncodingList().add(Encoding.INVERTED_INDEX); |
| } |
| } else { |
| if (hasEncoding(Encoding.INVERTED_INDEX)) { |
| this.getEncodingList().remove(Encoding.INVERTED_INDEX); |
| } |
| } |
| } |
| |
| /** |
| * Set the scale if it is decimal type |
| */ |
| public void setScale(int scale) { |
| this.scale = scale; |
| if (DataTypes.isDecimal(dataType)) { |
| ((DecimalType) dataType).setScale(scale); |
| } |
| } |
| |
| /** |
| * @return the scale |
| */ |
| public int getScale() { |
| return scale; |
| } |
| |
| /** |
| * Set the precision if it is decimal type |
| */ |
| public void setPrecision(int precision) { |
| this.precision = precision; |
| if (DataTypes.isDecimal(dataType)) { |
| ((DecimalType) dataType).setPrecision(precision); |
| } |
| } |
| |
| /** |
| * @return the precision |
| */ |
| public int getPrecision() { |
| return precision; |
| } |
| |
| /** |
| * @return the getNumberOfChild |
| */ |
| public int getNumberOfChild() { |
| return numberOfChild; |
| } |
| |
| /** |
| * @param numberOfChild the getNumberOfChild to set |
| */ |
| public void setNumberOfChild(int numberOfChild) { |
| this.numberOfChild = numberOfChild; |
| } |
| |
| /** |
| * @return the defaultValue |
| */ |
| public byte[] getDefaultValue() { |
| return defaultValue; |
| } |
| |
| /** |
| * @param defaultValue the defaultValue to set |
| */ |
| public void setDefaultValue(byte[] defaultValue) { |
| this.defaultValue = defaultValue; |
| } |
| |
| public List<ParentColumnTableRelation> getParentColumnTableRelations() { |
| return parentColumnTableRelations; |
| } |
| |
| public void setParentColumnTableRelations( |
| List<ParentColumnTableRelation> parentColumnTableRelations) { |
| this.parentColumnTableRelations = parentColumnTableRelations; |
| } |
| |
| /** |
| * hash code method to check get the hashcode based. |
| * for generating the hash code only column name and column unique id will considered |
| */ |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ((columnName == null) ? 0 : columnName.hashCode()) + |
| ((dataType == null) ? 0 : dataType.hashCode()); |
| return result; |
| } |
| |
| public int strictHashCode() { |
| return hashCode() + columnUniqueId.hashCode() + encodingList.hashCode(); |
| } |
| |
| /** |
| * Overridden equals method for columnSchema |
| */ |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (!(obj instanceof ColumnSchema)) { |
| return false; |
| } |
| ColumnSchema other = (ColumnSchema) obj; |
| if (columnName == null) { |
| if (other.columnName != null) { |
| return false; |
| } |
| } else if (!columnName.equalsIgnoreCase(other.columnName)) { |
| return false; |
| } |
| if (dataType == null) { |
| if (other.dataType != null) { |
| return false; |
| } |
| } else if (!dataType.equals(other.dataType)) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * method to compare columnSchema, |
| * other parameters along with just column name and column data type |
| * @param obj |
| * @return |
| */ |
| public boolean equalsWithStrictCheck(Object obj) { |
| if (!this.equals(obj)) { |
| return false; |
| } |
| ColumnSchema other = (ColumnSchema) obj; |
| if (!columnUniqueId.equals(other.columnUniqueId) || |
| (isDimensionColumn != other.isDimensionColumn) || |
| (isSortColumn != other.isSortColumn)) { |
| return false; |
| } |
| if (encodingList.size() != other.encodingList.size()) { |
| return false; |
| } |
| for (int i = 0; i < encodingList.size(); i++) { |
| if (encodingList.get(i).compareTo(other.encodingList.get(i)) != 0) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @return the dataType |
| */ |
| public DataType getDataType() { |
| return dataType; |
| } |
| |
| /** |
| * @param dataType the dataType to set |
| */ |
| public void setDataType(DataType dataType) { |
| this.dataType = dataType; |
| } |
| |
| /** |
| * @return the encoderList |
| */ |
| public List<Encoding> getEncodingList() { |
| return encodingList; |
| } |
| |
| /** |
| */ |
| public void setEncodingList(List<Encoding> encodingList) { |
| this.encodingList = encodingList; |
| } |
| |
| /** |
| * @param encoding |
| * @return true if contains the passing encoding |
| */ |
| public boolean hasEncoding(Encoding encoding) { |
| if (encodingList == null || encodingList.isEmpty()) { |
| return false; |
| } else { |
| return encodingList.contains(encoding); |
| } |
| } |
| |
| /** |
| * @param columnProperties |
| */ |
| public void setColumnProperties(Map<String, String> columnProperties) { |
| this.columnProperties = columnProperties; |
| } |
| |
| /** |
| * return column properties |
| */ |
| public Map<String, String> getColumnProperties() { |
| return columnProperties; |
| } |
| |
| /** |
| * return the visibility |
| * @return |
| */ |
| public boolean isInvisible() { |
| return invisible; |
| } |
| |
| /** |
| * set the visibility |
| * @param invisible |
| */ |
| public void setInvisible(boolean invisible) { |
| this.invisible = invisible; |
| } |
| |
| /** |
| * @return columnReferenceId |
| */ |
| public String getColumnReferenceId() { |
| return columnReferenceId; |
| } |
| |
| /** |
| * @param columnReferenceId |
| */ |
| public void setColumnReferenceId(String columnReferenceId) { |
| this.columnReferenceId = columnReferenceId; |
| } |
| |
| public int getSchemaOrdinal() { |
| return schemaOrdinal; |
| } |
| |
| public void setSchemaOrdinal(int schemaOrdinal) { |
| this.schemaOrdinal = schemaOrdinal; |
| } |
| |
| public boolean isSortColumn() { |
| return isSortColumn; |
| } |
| |
| public void setSortColumn(boolean sortColumn) { |
| isSortColumn = sortColumn; |
| } |
| |
| public String getAggFunction() { |
| return aggFunction; |
| } |
| |
| public void setFunction(String function) { |
| if (null == function) { |
| return; |
| } |
| if (TimeSeriesUDF.INSTANCE.TIMESERIES_FUNCTION.contains(function.toLowerCase())) { |
| this.timeSeriesFunction = function; |
| } else { |
| this.aggFunction = function; |
| } |
| } |
| |
| public String getTimeSeriesFunction() { |
| return timeSeriesFunction; |
| } |
| |
| @Override |
| public void write(DataOutput out) throws IOException { |
| out.writeShort(dataType.getId()); |
| out.writeUTF(columnName); |
| out.writeUTF(columnUniqueId); |
| out.writeUTF(columnReferenceId); |
| if (encodingList == null) { |
| out.writeShort(0); |
| } else { |
| out.writeShort(encodingList.size()); |
| for (Encoding encoding : encodingList) { |
| out.writeShort(encoding.ordinal()); |
| } |
| } |
| out.writeBoolean(isDimensionColumn); |
| if (DataTypes.isDecimal(dataType)) { |
| out.writeInt(((DecimalType) dataType).getScale()); |
| out.writeInt(((DecimalType) dataType).getPrecision()); |
| } else { |
| out.writeInt(-1); |
| out.writeInt(-1); |
| } |
| out.writeInt(schemaOrdinal); |
| out.writeInt(numberOfChild); |
| WritableUtil.writeByteArray(out, defaultValue); |
| if (columnProperties == null) { |
| out.writeShort(0); |
| } else { |
| out.writeShort(columnProperties.size()); |
| for (Map.Entry<String, String> entry : columnProperties.entrySet()) { |
| out.writeUTF(entry.getKey()); |
| out.writeUTF(entry.getValue()); |
| } |
| } |
| out.writeBoolean(invisible); |
| out.writeBoolean(isSortColumn); |
| out.writeUTF(null != aggFunction ? aggFunction : ""); |
| out.writeUTF(timeSeriesFunction); |
| boolean isParentTableColumnRelationExists = |
| null != parentColumnTableRelations && parentColumnTableRelations.size() > 0; |
| out.writeBoolean(isParentTableColumnRelationExists); |
| if (isParentTableColumnRelationExists) { |
| out.writeShort(parentColumnTableRelations.size()); |
| for (int i = 0; i < parentColumnTableRelations.size(); i++) { |
| parentColumnTableRelations.get(i).write(out); |
| } |
| } |
| out.writeBoolean(isLocalDictColumn); |
| out.writeBoolean(spatialColumn); |
| } |
| |
| @Override |
| public void readFields(DataInput in) throws IOException { |
| int id = in.readShort(); |
| this.dataType = DataTypes.valueOf(id); |
| this.columnName = in.readUTF(); |
| this.columnUniqueId = in.readUTF(); |
| this.columnReferenceId = in.readUTF(); |
| int encodingListSize = in.readShort(); |
| this.encodingList = new ArrayList<>(encodingListSize); |
| for (int i = 0; i < encodingListSize; i++) { |
| id = in.readShort(); |
| encodingList.add(Encoding.valueOf(id)); |
| } |
| this.isDimensionColumn = in.readBoolean(); |
| this.scale = in.readInt(); |
| this.precision = in.readInt(); |
| if (DataTypes.isDecimal(dataType)) { |
| DecimalType decimalType = (DecimalType) dataType; |
| decimalType.setPrecision(precision); |
| decimalType.setScale(scale); |
| } |
| this.schemaOrdinal = in.readInt(); |
| this.numberOfChild = in.readInt(); |
| this.defaultValue = WritableUtil.readByteArray(in); |
| int mapSize = in.readShort(); |
| this.columnProperties = new HashMap<>(mapSize); |
| for (int i = 0; i < mapSize; i++) { |
| String key = in.readUTF(); |
| String value = in.readUTF(); |
| this.columnProperties.put(key, value); |
| } |
| this.invisible = in.readBoolean(); |
| this.isSortColumn = in.readBoolean(); |
| this.aggFunction = in.readUTF(); |
| this.timeSeriesFunction = in.readUTF(); |
| boolean isParentTableColumnRelationExists = in.readBoolean(); |
| if (isParentTableColumnRelationExists) { |
| short parentColumnTableRelationSize = in.readShort(); |
| this.parentColumnTableRelations = new ArrayList<>(parentColumnTableRelationSize); |
| for (int i = 0; i < parentColumnTableRelationSize; i++) { |
| ParentColumnTableRelation parentColumnTableRelation = |
| new ParentColumnTableRelation(null, null, null); |
| parentColumnTableRelation.readFields(in); |
| parentColumnTableRelations.add(parentColumnTableRelation); |
| } |
| } |
| this.isLocalDictColumn = in.readBoolean(); |
| this.spatialColumn = in.readBoolean(); |
| } |
| |
| /** |
| * returns whether column is complex column based on column name for child column |
| * @return |
| */ |
| public boolean isComplexColumn() { |
| return this.getColumnName() |
| .contains(".val") || this.getColumnName().contains("."); |
| } |
| |
| public ColumnSchema clone() { |
| try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| DataOutputStream dos = new DataOutputStream(bos)) { |
| this.write(dos); |
| DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); |
| ColumnSchema columnSchema = (ColumnSchema) super.clone(); |
| columnSchema.readFields(dis); |
| return columnSchema; |
| } catch (IOException | CloneNotSupportedException e) { |
| throw new RuntimeException("Error occur while cloning ColumnSchema", e); |
| } |
| } |
| |
| /** |
| * Checks whether it is a spatial index column. |
| * @return Returns True if the column is a spatial index column. Otherwise returns False. |
| */ |
| public boolean isSpatialColumn() { |
| return spatialColumn; |
| } |
| |
| /** |
| * Set the column spatial index property. True or False to indicate column is a spatial index |
| * column or not respectively. |
| * @param spatialColumn True or False |
| */ |
| public void setSpatialColumn(boolean spatialColumn) { |
| this.spatialColumn = spatialColumn; |
| } |
| } |