diff --git a/java/common/src/main/java/org/apache/tsfile/block/column/Column.java b/java/common/src/main/java/org/apache/tsfile/block/column/Column.java index c9e30d200..7cf721bed 100644 --- a/java/common/src/main/java/org/apache/tsfile/block/column/Column.java +++ b/java/common/src/main/java/org/apache/tsfile/block/column/Column.java @@ -23,6 +23,9 @@ import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Arrays; public interface Column { @@ -33,6 +36,9 @@ public interface Column { /** Get the encoding for this column. */ ColumnEncoding getEncoding(); + /** Converts this column to the specified data type. */ + Column convertTo(TSDataType type); + /** Gets a boolean at {@code position}. */ default boolean getBoolean(int position) { throw new UnsupportedOperationException(getClass().getName()); @@ -108,6 +114,33 @@ default TsPrimitiveType getTsPrimitiveType(int position) { throw new UnsupportedOperationException(getClass().getName()); } + /** Serializes the value at {@code index} to {@code buffer}. */ + default void writeTo(int index, ByteBuffer buffer) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Serializes the value at {@code index} to {@code stream}. */ + default void writeTo(int index, DataOutputStream stream) throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Serializes all non-null values in this column. */ + default void serializeWithoutNulls(DataOutputStream output) throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns whether the values at the two positions are equal. */ + default boolean arePositionsEqual(int pos1, int pos2) { + return arePositionsEqual(pos1, this, pos2); + } + + /** + * Returns whether this value and the value in another column are equal. + * + *

For non-null values, this column and {@code that} must provide compatible value accessors. + */ + boolean arePositionsEqual(int thisPos, Column that, int thatPos); + /** * Is it possible the column may have a null value? If false, the column cannot contain a null, * but if true, the column may or may not have a null. diff --git a/java/common/src/main/java/org/apache/tsfile/enums/TSDataType.java b/java/common/src/main/java/org/apache/tsfile/enums/TSDataType.java index f97291046..eaa09be73 100644 --- a/java/common/src/main/java/org/apache/tsfile/enums/TSDataType.java +++ b/java/common/src/main/java/org/apache/tsfile/enums/TSDataType.java @@ -39,45 +39,95 @@ public enum TSDataType { /** BOOLEAN. */ - BOOLEAN((byte) 0), + BOOLEAN( + (byte) 0, + 1, + false, + true, + TSDataType::castIdenticalSingleValue, + TSDataType::castIdenticalArray), /** INT32. */ - INT32((byte) 1), + INT32( + (byte) 1, + 4, + true, + true, + TSDataType::castIdenticalSingleValue, + TSDataType::castIdenticalArray), /** INT64. */ - INT64((byte) 2), + INT64((byte) 2, 8, true, true, TSDataType::castToLongSingleValue, TSDataType::castToLongArray), /** FLOAT. */ - FLOAT((byte) 3), + FLOAT((byte) 3, 4, true, true, TSDataType::castToFloatSingleValue, TSDataType::castToFloatArray), /** DOUBLE. */ - DOUBLE((byte) 4), + DOUBLE( + (byte) 4, 8, true, true, TSDataType::castToDoubleSingleValue, TSDataType::castToDoubleArray), /** TEXT. */ - TEXT((byte) 5), + TEXT((byte) 5, 8, false, true, TSDataType::castToTextSingleValue, TSDataType::castToTextArray), /** VECTOR. */ - VECTOR((byte) 6), + VECTOR( + (byte) 6, + 8, + false, + false, + TSDataType::unsupportedSingleValueCast, + TSDataType::unsupportedArrayCast), /** UNKNOWN. */ - UNKNOWN((byte) 7), + UNKNOWN( + (byte) 7, + TSDataType.UNSUPPORTED_DATA_TYPE_SIZE, + false, + false, + TSDataType::unsupportedSingleValueCast, + TSDataType::unsupportedArrayCast), /** TIMESTAMP. */ - TIMESTAMP((byte) 8), + TIMESTAMP( + (byte) 8, + 8, + false, + true, + TSDataType::castToTimestampSingleValue, + TSDataType::castToTimestampArray), /** DATE. */ - DATE((byte) 9), + DATE( + (byte) 9, + 4, + false, + true, + TSDataType::castIdenticalSingleValue, + TSDataType::castIdenticalArray), /** BLOB. */ - BLOB((byte) 10), + BLOB((byte) 10, 8, false, false, TSDataType::castToBlobSingleValue, TSDataType::castToBlobArray), /** STRING */ - STRING((byte) 11), + STRING((byte) 11, 8, false, true, TSDataType::castToTextSingleValue, TSDataType::castToTextArray), /** OBJECT */ - OBJECT((byte) 12); - + OBJECT( + (byte) 12, + 8, + false, + false, + TSDataType::castIdenticalSingleValue, + TSDataType::castIdenticalArray); + + private static final int UNSUPPORTED_DATA_TYPE_SIZE = -1; + private static final Object UNSUPPORTED_CAST = new Object(); private final byte type; + private final int dataTypeSize; + private final boolean numeric; + private final boolean comparable; + private final SingleValueCaster singleValueCaster; + private final ArrayCaster arrayCaster; private static final Map> compatibleTypes; static { @@ -147,8 +197,19 @@ public enum TSDataType { compatibleTypes.put(OBJECT, Collections.emptySet()); } - TSDataType(byte type) { + TSDataType( + byte type, + int dataTypeSize, + boolean numeric, + boolean comparable, + SingleValueCaster singleValueCaster, + ArrayCaster arrayCaster) { this.type = type; + this.dataTypeSize = dataTypeSize; + this.numeric = numeric; + this.comparable = comparable; + this.singleValueCaster = singleValueCaster; + this.arrayCaster = arrayCaster; } /** @@ -211,271 +272,230 @@ public Object castFromSingleValue(TSDataType sourceType, Object value) { if (Objects.isNull(value)) { return null; } - switch (this) { - case BOOLEAN: - if (sourceType == TSDataType.BOOLEAN) { - return value; - } else { - break; - } - case INT32: - if (sourceType == TSDataType.INT32) { - return value; - } else { - break; - } - case INT64: - if (sourceType == TSDataType.INT64) { - return value; - } else if (sourceType == INT32) { - return (long) ((int) value); - } else if (sourceType == TIMESTAMP) { - return value; - } else { - break; - } - case FLOAT: - if (sourceType == TSDataType.FLOAT) { - return value; - } else if (sourceType == INT32) { - return (float) ((int) value); - } else { - break; - } - case DOUBLE: - if (sourceType == TSDataType.DOUBLE) { - return value; - } else if (sourceType == INT32) { - return (double) ((int) value); - } else if (sourceType == INT64) { - return (double) ((long) value); - } else if (sourceType == FLOAT) { - return (double) ((float) value); - } else if (sourceType == TIMESTAMP) { - return (double) ((long) value); - } else { - break; - } - case TEXT: - if (sourceType == TSDataType.TEXT || sourceType == TSDataType.STRING) { - return value; - } else if (sourceType == TSDataType.INT32 - || sourceType == TSDataType.INT64 - || sourceType == TSDataType.FLOAT - || sourceType == TSDataType.DOUBLE - || sourceType == TSDataType.BOOLEAN - || sourceType == TSDataType.TIMESTAMP) { - return new Binary(String.valueOf(value), StandardCharsets.UTF_8); - } else if (sourceType == TSDataType.DATE) { - return new Binary(getDateStringValue((int) value), StandardCharsets.UTF_8); - } else if (sourceType == TSDataType.BLOB) { - return new Binary(value.toString(), StandardCharsets.UTF_8); - } else { - break; - } - case TIMESTAMP: - if (sourceType == TSDataType.TIMESTAMP) { - return value; - } else if (sourceType == INT32) { - return (long) ((int) value); - } else if (sourceType == INT64) { - return value; - } else { - break; - } - case DATE: - if (sourceType == TSDataType.DATE) { - return value; - } else { - break; - } - case BLOB: - if (sourceType == TSDataType.BLOB - || sourceType == TSDataType.STRING - || sourceType == TSDataType.TEXT) { - return value; - } else { - break; - } - case STRING: - if (sourceType == TSDataType.STRING || sourceType == TSDataType.TEXT) { - return value; - } else if (sourceType == TSDataType.INT32 - || sourceType == TSDataType.INT64 - || sourceType == TSDataType.FLOAT - || sourceType == TSDataType.DOUBLE - || sourceType == TSDataType.BOOLEAN - || sourceType == TSDataType.TIMESTAMP) { - return new Binary(String.valueOf(value), StandardCharsets.UTF_8); - } else if (sourceType == TSDataType.DATE) { - return new Binary(getDateStringValue((int) value), StandardCharsets.UTF_8); - } else if (sourceType == TSDataType.BLOB) { - return new Binary(value.toString(), StandardCharsets.UTF_8); - } else { - break; - } - case OBJECT: - if (sourceType == TSDataType.OBJECT) { - return value; - } else { - break; - } - case VECTOR: - case UNKNOWN: - default: - break; + Object result = singleValueCaster.cast(this, sourceType, value); + if (result != UNSUPPORTED_CAST) { + return result; } throw new ClassCastException( Messages.format("error.common.unsupported_cast", sourceType, this)); } - @SuppressWarnings({"java:S3012", "java:S3776", "java:S6541"}) + private static Object castIdenticalSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + return sourceType == targetType ? value : UNSUPPORTED_CAST; + } + + private static Object castToLongSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + if (sourceType == INT64 || sourceType == TIMESTAMP) { + return value; + } + return sourceType == INT32 ? (long) ((int) value) : UNSUPPORTED_CAST; + } + + private static Object castToFloatSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + if (sourceType == FLOAT) { + return value; + } + return sourceType == INT32 ? (float) ((int) value) : UNSUPPORTED_CAST; + } + + private static Object castToDoubleSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + if (sourceType == DOUBLE) { + return value; + } + if (sourceType == INT32) { + return (double) ((int) value); + } + if (sourceType == INT64 || sourceType == TIMESTAMP) { + return (double) ((long) value); + } + return sourceType == FLOAT ? (double) ((float) value) : UNSUPPORTED_CAST; + } + + private static Object castToTextSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + if (sourceType == TEXT || sourceType == STRING) { + return value; + } + if (sourceType == INT32 + || sourceType == INT64 + || sourceType == FLOAT + || sourceType == DOUBLE + || sourceType == BOOLEAN + || sourceType == TIMESTAMP) { + return new Binary(String.valueOf(value), StandardCharsets.UTF_8); + } + if (sourceType == DATE) { + return new Binary(getDateStringValue((int) value), StandardCharsets.UTF_8); + } + return sourceType == BLOB + ? new Binary(value.toString(), StandardCharsets.UTF_8) + : UNSUPPORTED_CAST; + } + + private static Object castToTimestampSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + if (sourceType == TIMESTAMP || sourceType == INT64) { + return value; + } + return sourceType == INT32 ? (long) ((int) value) : UNSUPPORTED_CAST; + } + + private static Object castToBlobSingleValue( + TSDataType targetType, TSDataType sourceType, Object value) { + return sourceType == BLOB || sourceType == STRING || sourceType == TEXT + ? value + : UNSUPPORTED_CAST; + } + + private static Object unsupportedSingleValueCast( + TSDataType targetType, TSDataType sourceType, Object value) { + return UNSUPPORTED_CAST; + } + public Object castFromArray(TSDataType sourceType, Object array) { - switch (this) { - case BOOLEAN: - if (sourceType == TSDataType.BOOLEAN) { - return array; - } else { - break; - } - case INT32: - if (sourceType == TSDataType.INT32) { - return array; - } else { - break; - } - case INT64: - if (sourceType == TSDataType.INT64) { - return array; - } else if (sourceType == INT32) { - return Arrays.stream((int[]) array).mapToLong(Long::valueOf).toArray(); - } else if (sourceType == TIMESTAMP) { - return array; - } else { - break; - } - case FLOAT: - if (sourceType == TSDataType.FLOAT) { - return array; - } else if (sourceType == INT32) { - int[] tmp = (int[]) array; - float[] result = new float[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = tmp[i]; - } - return result; - } else { - break; - } - case DOUBLE: - if (sourceType == TSDataType.DOUBLE) { - return array; - } else if (sourceType == INT32) { - return Arrays.stream((int[]) array).mapToDouble(Double::valueOf).toArray(); - } else if (sourceType == INT64) { - return Arrays.stream((long[]) array).mapToDouble(Double::valueOf).toArray(); - } else if (sourceType == FLOAT) { - float[] tmp = (float[]) array; - double[] result = new double[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = tmp[i]; - } - return result; - } else if (sourceType == TIMESTAMP) { - return Arrays.stream((long[]) array).mapToDouble(Double::valueOf).toArray(); - } else { - break; - } - case TIMESTAMP: - if (sourceType == TSDataType.TIMESTAMP) { - return array; - } else if (sourceType == INT32) { - return Arrays.stream((int[]) array).mapToLong(Long::valueOf).toArray(); - } else if (sourceType == INT64) { - return array; - } else { - break; - } - case DATE: - if (sourceType == TSDataType.DATE) { - return array; - } else { - break; - } - case BLOB: - if (sourceType == TSDataType.BLOB - || sourceType == TSDataType.STRING - || sourceType == TSDataType.TEXT) { - return array; - } else { - break; - } - case TEXT: - case STRING: - if (sourceType == TSDataType.STRING - || sourceType == TSDataType.TEXT - || sourceType == TSDataType.BLOB) { - return array; - } else if (sourceType == TSDataType.INT32) { - int[] tmp = (int[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(String.valueOf(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else if (sourceType == TSDataType.DATE) { - int[] tmp = (int[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(TSDataType.getDateStringValue(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else if (sourceType == TSDataType.INT64 || sourceType == TSDataType.TIMESTAMP) { - long[] tmp = (long[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(String.valueOf(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else if (sourceType == TSDataType.FLOAT) { - float[] tmp = (float[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(String.valueOf(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else if (sourceType == TSDataType.DOUBLE) { - double[] tmp = (double[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(String.valueOf(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else if (sourceType == TSDataType.BOOLEAN) { - boolean[] tmp = (boolean[]) array; - Binary[] result = new Binary[tmp.length]; - for (int i = 0; i < tmp.length; i++) { - result[i] = new Binary(String.valueOf(tmp[i]), StandardCharsets.UTF_8); - } - return result; - } else { - break; - } - case OBJECT: - if (sourceType == TSDataType.OBJECT) { - return array; - } else { - break; - } - case VECTOR: - case UNKNOWN: - default: - break; + Object result = arrayCaster.cast(this, sourceType, array); + if (result != UNSUPPORTED_CAST) { + return result; } throw new ClassCastException( Messages.format("error.common.unsupported_cast", sourceType, this)); } + private static Object castIdenticalArray( + TSDataType targetType, TSDataType sourceType, Object array) { + return sourceType == targetType ? array : UNSUPPORTED_CAST; + } + + private static Object castToLongArray( + TSDataType targetType, TSDataType sourceType, Object array) { + if (sourceType == INT64 || sourceType == TIMESTAMP) { + return array; + } + return sourceType == INT32 + ? Arrays.stream((int[]) array).mapToLong(Long::valueOf).toArray() + : UNSUPPORTED_CAST; + } + + private static Object castToFloatArray( + TSDataType targetType, TSDataType sourceType, Object array) { + if (sourceType == FLOAT) { + return array; + } + if (sourceType != INT32) { + return UNSUPPORTED_CAST; + } + int[] values = (int[]) array; + float[] result = new float[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = values[i]; + } + return result; + } + + private static Object castToDoubleArray( + TSDataType targetType, TSDataType sourceType, Object array) { + if (sourceType == DOUBLE) { + return array; + } + if (sourceType == INT32) { + return Arrays.stream((int[]) array).mapToDouble(Double::valueOf).toArray(); + } + if (sourceType == INT64 || sourceType == TIMESTAMP) { + return Arrays.stream((long[]) array).mapToDouble(Double::valueOf).toArray(); + } + if (sourceType != FLOAT) { + return UNSUPPORTED_CAST; + } + float[] values = (float[]) array; + double[] result = new double[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = values[i]; + } + return result; + } + + private static Object castToTimestampArray( + TSDataType targetType, TSDataType sourceType, Object array) { + if (sourceType == TIMESTAMP || sourceType == INT64) { + return array; + } + return sourceType == INT32 + ? Arrays.stream((int[]) array).mapToLong(Long::valueOf).toArray() + : UNSUPPORTED_CAST; + } + + private static Object castToBlobArray( + TSDataType targetType, TSDataType sourceType, Object array) { + return sourceType == BLOB || sourceType == STRING || sourceType == TEXT + ? array + : UNSUPPORTED_CAST; + } + + private static Object castToTextArray( + TSDataType targetType, TSDataType sourceType, Object array) { + if (sourceType == STRING || sourceType == TEXT || sourceType == BLOB) { + return array; + } + if (sourceType == INT32) { + int[] values = (int[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(String.valueOf(values[i]), StandardCharsets.UTF_8); + } + return result; + } + if (sourceType == DATE) { + int[] values = (int[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(getDateStringValue(values[i]), StandardCharsets.UTF_8); + } + return result; + } + if (sourceType == INT64 || sourceType == TIMESTAMP) { + long[] values = (long[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(String.valueOf(values[i]), StandardCharsets.UTF_8); + } + return result; + } + if (sourceType == FLOAT) { + float[] values = (float[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(String.valueOf(values[i]), StandardCharsets.UTF_8); + } + return result; + } + if (sourceType == DOUBLE) { + double[] values = (double[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(String.valueOf(values[i]), StandardCharsets.UTF_8); + } + return result; + } + if (sourceType == BOOLEAN) { + boolean[] values = (boolean[]) array; + Binary[] result = new Binary[values.length]; + for (int i = 0; i < values.length; i++) { + result[i] = new Binary(String.valueOf(values[i]), StandardCharsets.UTF_8); + } + return result; + } + return UNSUPPORTED_CAST; + } + + private static Object unsupportedArrayCast( + TSDataType targetType, TSDataType sourceType, Object array) { + return UNSUPPORTED_CAST; + } + public static TSDataType deserializeFrom(ByteBuffer buffer) { return deserialize(buffer.get()); } @@ -501,26 +521,10 @@ public void serializeTo(FileOutputStream outputStream) throws IOException { } public int getDataTypeSize() { - switch (this) { - case BOOLEAN: - return 1; - case INT32: - case FLOAT: - case DATE: - return 4; - // For text: return the size of reference here - case TEXT: - case INT64: - case DOUBLE: - case VECTOR: - case BLOB: - case OBJECT: - case STRING: - case TIMESTAMP: - return 8; - default: - throw new UnSupportedDataTypeException(this.toString()); + if (dataTypeSize == UNSUPPORTED_DATA_TYPE_SIZE) { + throw new UnSupportedDataTypeException(this.toString()); } + return dataTypeSize; } /** @@ -539,25 +543,10 @@ public byte serialize() { * @throws UnSupportedDataTypeException when meets unSupported DataType */ public boolean isNumeric() { - switch (this) { - case INT32: - case INT64: - case FLOAT: - case DOUBLE: - return true; - // For text: return the size of reference here - case BLOB: - case TIMESTAMP: - case DATE: - case STRING: - case BOOLEAN: - case TEXT: - case VECTOR: - case OBJECT: - return false; - default: - throw new UnSupportedDataTypeException(this.toString()); + if (this == UNKNOWN) { + throw new UnSupportedDataTypeException(this.toString()); } + return numeric; } /** @@ -567,24 +556,10 @@ public boolean isNumeric() { * @throws UnSupportedDataTypeException when meets unSupported DataType */ public boolean isComparable() { - switch (this) { - case INT32: - case INT64: - case FLOAT: - case DOUBLE: - case TEXT: - case BOOLEAN: - case TIMESTAMP: - case DATE: - case STRING: - return true; - case VECTOR: - case BLOB: - case OBJECT: - return false; - default: - throw new UnSupportedDataTypeException(this.toString()); + if (this == UNKNOWN) { + throw new UnSupportedDataTypeException(this.toString()); } + return comparable; } public boolean isBinary() { @@ -603,4 +578,16 @@ public boolean hasNoValueInStatistics() { public static String getDateStringValue(int value) { return String.format("%04d-%02d-%02d", value / 10000, (value % 10000) / 100, value % 100); } + + @FunctionalInterface + private interface SingleValueCaster { + + Object cast(TSDataType targetType, TSDataType sourceType, Object value); + } + + @FunctionalInterface + private interface ArrayCaster { + + Object cast(TSDataType targetType, TSDataType sourceType, Object array); + } } diff --git a/java/common/src/main/java/org/apache/tsfile/utils/TsPrimitiveType.java b/java/common/src/main/java/org/apache/tsfile/utils/TsPrimitiveType.java index fa88e68c6..4b20ffbab 100644 --- a/java/common/src/main/java/org/apache/tsfile/utils/TsPrimitiveType.java +++ b/java/common/src/main/java/org/apache/tsfile/utils/TsPrimitiveType.java @@ -28,73 +28,6 @@ public abstract class TsPrimitiveType implements Serializable { - /** - * get tsPrimitiveType by resultDataType. - * - * @param dataType given TsDataType - */ - public static TsPrimitiveType getByType(TSDataType dataType) { - switch (dataType) { - case BOOLEAN: - return new TsPrimitiveType.TsBoolean(); - case INT32: - return new TsPrimitiveType.TsInt(); - case DATE: - return new TsPrimitiveType.TsInt(TSDataType.DATE); - case INT64: - case TIMESTAMP: - return new TsPrimitiveType.TsLong(); - case FLOAT: - return new TsPrimitiveType.TsFloat(); - case DOUBLE: - return new TsPrimitiveType.TsDouble(); - case TEXT: - case BLOB: - case STRING: - case OBJECT: - return new TsPrimitiveType.TsBinary(); - case VECTOR: - return new TsPrimitiveType.TsVector(); - default: - throw new UnSupportedDataTypeException( - Messages.format("error.common.unsupported_data_type", dataType)); - } - } - - /** - * get tsPrimitiveType by resultDataType and initial value. - * - * @param dataType given TsDataType - * @param v initial value - */ - public static TsPrimitiveType getByType(TSDataType dataType, Object v) { - switch (dataType) { - case BOOLEAN: - return new TsPrimitiveType.TsBoolean((boolean) v); - case INT32: - return new TsPrimitiveType.TsInt((int) v); - case DATE: - return new TsPrimitiveType.TsInt((int) v, TSDataType.DATE); - case INT64: - case TIMESTAMP: - return new TsPrimitiveType.TsLong((long) v); - case FLOAT: - return new TsPrimitiveType.TsFloat((float) v); - case DOUBLE: - return new TsPrimitiveType.TsDouble((double) v); - case TEXT: - case BLOB: - case STRING: - case OBJECT: - return new TsPrimitiveType.TsBinary((Binary) v); - case VECTOR: - return new TsPrimitiveType.TsVector((TsPrimitiveType[]) v); - default: - throw new UnSupportedDataTypeException( - Messages.format("error.common.unsupported_data_type", dataType)); - } - } - public boolean getBoolean() { throw new UnsupportedOperationException( Messages.format("error.common.subclass_op_not_supported", "getBoolean()")); @@ -182,6 +115,36 @@ public void setVector(TsPrimitiveType[] val) { public abstract TSDataType getDataType(); + public abstract void copy(TsPrimitiveType value); + + private static TsPrimitiveType copyValue(TsPrimitiveType value) { + if (value == null) { + return null; + } + if (value instanceof TsBoolean) { + return new TsBoolean((TsBoolean) value); + } + if (value instanceof TsInt) { + return new TsInt((TsInt) value); + } + if (value instanceof TsLong) { + return new TsLong((TsLong) value); + } + if (value instanceof TsFloat) { + return new TsFloat((TsFloat) value); + } + if (value instanceof TsDouble) { + return new TsDouble((TsDouble) value); + } + if (value instanceof TsBinary) { + return new TsBinary((TsBinary) value); + } + if (value instanceof TsVector) { + return new TsVector((TsVector) value); + } + throw new UnSupportedDataTypeException(String.valueOf(value.getDataType())); + } + @Override public String toString() { return getStringValue(); @@ -208,6 +171,10 @@ public TsBoolean(boolean value) { this.value = value; } + public TsBoolean(TsBoolean other) { + this.value = other.value; + } + @Override public boolean getBoolean() { return value; @@ -233,6 +200,15 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsBoolean", "Boolean")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setBoolean(value.getBoolean()); + } + @Override public void reset() { value = false; @@ -297,6 +273,11 @@ public TsInt(int value, TSDataType dataType) { this.dataType = dataType; } + public TsInt(TsInt other) { + this.value = other.value; + this.dataType = other.dataType; + } + @Override public int getInt() { return value; @@ -304,12 +285,12 @@ public int getInt() { @Override public double getDouble() { - return (double) value; + return value; } @Override public long getLong() { - return (long) value; + return value; } @Override @@ -319,6 +300,9 @@ public float getFloat() { @Override public Binary getBinary() { + if (dataType == TSDataType.DATE) { + return new Binary(TSDataType.getDateStringValue(value), StandardCharsets.UTF_8); + } return new Binary(String.valueOf(this.value), StandardCharsets.UTF_8); } @@ -337,6 +321,16 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsInt", "Integer")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setInt(value.getInt()); + dataType = value.getDataType(); + } + @Override public void reset() { value = 0; @@ -390,6 +384,10 @@ public TsLong(long value) { this.value = value; } + public TsLong(TsLong other) { + this.value = other.value; + } + @Override public long getLong() { return value; @@ -420,6 +418,15 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsLong", "Long")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setLong(value.getLong()); + } + @Override public void reset() { value = 0; @@ -473,6 +480,10 @@ public TsFloat(float value) { this.value = value; } + public TsFloat(TsFloat other) { + this.value = other.value; + } + @Override public float getFloat() { return value; @@ -503,6 +514,15 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsFloat", "float")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setFloat(value.getFloat()); + } + @Override public void reset() { value = 0; @@ -556,6 +576,10 @@ public TsDouble(double value) { this.value = value; } + public TsDouble(TsDouble other) { + this.value = other.value; + } + @Override public double getDouble() { return value; @@ -581,6 +605,15 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsDouble", "Double")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setDouble(value.getDouble()); + } + @Override public void reset() { value = 0.0; @@ -634,6 +667,18 @@ public TsBinary(Binary value) { this.value = value; } + public TsBinary(TsBinary other) { + this.value = copyBinary(other.value); + } + + private static Binary copyBinary(Binary value) { + if (value == null) { + return null; + } + byte[] bytes = value.getValues(); + return new Binary(bytes == null ? null : Arrays.copyOf(bytes, bytes.length)); + } + @Override public Binary getBinary() { return value; @@ -654,6 +699,15 @@ public void setObject(Object val) { Messages.format("error.common.tsprimitive_wrong_value_type", "TsBinary", "Binary")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setBinary(copyBinary(value.getBinary())); + } + @Override public void reset() { value = null; @@ -707,6 +761,21 @@ public TsVector(TsPrimitiveType[] values) { this.values = values; } + public TsVector(TsVector other) { + this.values = copyValues(other.values); + } + + private static TsPrimitiveType[] copyValues(TsPrimitiveType[] values) { + if (values == null) { + return null; + } + TsPrimitiveType[] copiedValues = new TsPrimitiveType[values.length]; + for (int i = 0; i < values.length; i++) { + copiedValues[i] = copyValue(values[i]); + } + return copiedValues; + } + @Override public TsPrimitiveType[] getVector() { return values; @@ -728,6 +797,15 @@ public void setObject(Object val) { "error.common.tsprimitive_wrong_value_type", "TsVector", "TsPrimitiveType[]")); } + @Override + public void copy(TsPrimitiveType value) { + if (value == null) { + reset(); + return; + } + setVector(copyValues(value.getVector())); + } + @Override public void reset() { values = null; diff --git a/java/common/src/main/java/org/apache/tsfile/write/UnSupportedDataTypeException.java b/java/common/src/main/java/org/apache/tsfile/write/UnSupportedDataTypeException.java index a7ab7dac1..2ec35339c 100644 --- a/java/common/src/main/java/org/apache/tsfile/write/UnSupportedDataTypeException.java +++ b/java/common/src/main/java/org/apache/tsfile/write/UnSupportedDataTypeException.java @@ -21,6 +21,10 @@ public class UnSupportedDataTypeException extends RuntimeException { + // true if the exception is raised by a datatype that is known to be unsupported, + // otherwise, it is caused by an unknown/uncovered datatype + private boolean isChecked = false; + public UnSupportedDataTypeException(String message) { super("Unsupported dataType: " + message); } @@ -28,4 +32,13 @@ public UnSupportedDataTypeException(String message) { public UnSupportedDataTypeException(String message, Throwable e) { super(message + e.getMessage()); } + + public UnSupportedDataTypeException setChecked(boolean checked) { + isChecked = checked; + return this; + } + + public boolean isChecked() { + return isChecked; + } } diff --git a/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties b/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties index 98909f7a6..59679f6fd 100644 --- a/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties +++ b/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties @@ -1320,6 +1320,18 @@ log.utils.bytes_int_to_bytes_error = tsfile-common BytesUtils: cannot convert an # BytesUtils.longToBytes — cannot convert long to byte array (3 args: srcNum, pos, width) log.utils.bytes_long_to_bytes_error = tsfile-common BytesUtils: cannot convert a long {} to a byte array, pos {}, width {} +# RecordUtils.parseSimpleTupleRecord — invalid timestamp +log.utils.record_timestamp_illegal = Given timestamp is illegal: {} + +# RecordUtils.parseSimpleTupleRecord — measurement type not found +log.utils.record_measurement_type_not_found = Type not found for measurement ID {}; skipping + +# RecordUtils.parseSimpleTupleRecord — failed to parse measurement +log.utils.record_parse_measurement_error = Failed to parse measurement; omitting it + +# RecordUtils.parseSimpleTupleRecord — unsupported data type +log.utils.record_unsupported_data_type = Unsupported data type: {} + # TsFileSketchTool — cannot load file (filename arg) error.utils.sketch_tool_cannot_load = Cannot load file %1$s because the file has crashed. diff --git a/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties b/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties index a1a437cfb..c2406b634 100644 --- a/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties +++ b/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties @@ -1320,6 +1320,18 @@ log.utils.bytes_int_to_bytes_error = tsfile-common BytesUtils: 无法将 integer # BytesUtils.longToBytes — cannot convert long to byte array (3 args: srcNum, pos, width) log.utils.bytes_long_to_bytes_error = tsfile-common BytesUtils: 无法将 long {} 转换为 byte 数组,pos {},width {} +# RecordUtils.parseSimpleTupleRecord — 时间戳非法 +log.utils.record_timestamp_illegal = 给定的时间戳非法:{} + +# RecordUtils.parseSimpleTupleRecord — 未找到测点类型 +log.utils.record_measurement_type_not_found = 未找到 measurement ID {} 的类型,跳过该测点 + +# RecordUtils.parseSimpleTupleRecord — 测点解析失败 +log.utils.record_parse_measurement_error = 解析测点时出错,已忽略 + +# RecordUtils.parseSimpleTupleRecord — 不支持的数据类型 +log.utils.record_unsupported_data_type = 不支持的数据类型:{} + # TsFileSketchTool — cannot load file (filename arg) error.utils.sketch_tool_cannot_load = 无法加载文件 %1$s,因为文件已损坏。 diff --git a/java/examples/src/main/java/org/apache/tsfile/DataGenerator.java b/java/examples/src/main/java/org/apache/tsfile/DataGenerator.java index 159e2ea65..d33f822c2 100644 --- a/java/examples/src/main/java/org/apache/tsfile/DataGenerator.java +++ b/java/examples/src/main/java/org/apache/tsfile/DataGenerator.java @@ -21,32 +21,38 @@ import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import java.time.LocalDate; public class DataGenerator { + + private static final TypeService GENERATE_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> index -> index % 2 == 0; + case INT32 -> index -> index; + case INT64, TIMESTAMP -> index -> (long) index; + case FLOAT -> index -> (float) index; + case DOUBLE -> index -> (double) index; + case DATE -> index -> LocalDate.of(2024, 1, index % 30 + 1); + case TEXT, STRING, BLOB -> + index -> new Binary(String.valueOf(index), TSFileConfig.STRING_CHARSET); + case OBJECT, ROW, UNKNOWN, VECTOR -> index -> null; + }; + + static { + GENERATE_VALUE_SERVICE.check(); + } + public static Object generate(TSDataType type, int index) { - switch (type) { - case INT32: - return index; - case INT64: - case TIMESTAMP: - return (long) index; - case FLOAT: - return (float) index; - case DOUBLE: - return (double) index; - case BOOLEAN: - return index % 2 == 0; - case DATE: - return LocalDate.of(2024, 1, index % 30 + 1); - case TEXT: - case STRING: - case BLOB: - return new Binary(String.valueOf(index), TSFileConfig.STRING_CHARSET); - default: - return null; - } + return GENERATE_VALUE_SERVICE.call(Type.fromTsDataType(type)).generate(index); + } + + @FunctionalInterface + private interface ValueGenerator { + Object generate(int index); } } diff --git a/java/examples/src/main/java/org/apache/tsfile/TsFileSequenceRead.java b/java/examples/src/main/java/org/apache/tsfile/TsFileSequenceRead.java index ecd3fdd27..5873784e7 100644 --- a/java/examples/src/main/java/org/apache/tsfile/TsFileSequenceRead.java +++ b/java/examples/src/main/java/org/apache/tsfile/TsFileSequenceRead.java @@ -34,6 +34,8 @@ import org.apache.tsfile.fileSystem.FSFactoryProducer; import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.read.reader.page.PageReader; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.apache.tsfile.write.UnSupportedDataTypeException; @@ -51,6 +53,25 @@ public class TsFileSequenceRead { public static final String POINT_IN_PAGE = "\t\tpoints in the page: "; private static int MASK = 0x80; + private static final TypeService PAGE_VALUE_READER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> Decoder::readBoolean; + case INT32, DATE -> Decoder::readInt; + case INT64, TIMESTAMP -> Decoder::readLong; + case FLOAT -> Decoder::readFloat; + case DOUBLE -> Decoder::readDouble; + case TEXT, STRING, BLOB -> Decoder::readBinary; + case OBJECT, ROW, UNKNOWN, VECTOR -> + (decoder, buffer) -> { + throw new UnSupportedDataTypeException(String.valueOf(type.getTypeEnum())); + }; + }; + + static { + PAGE_VALUE_READER_SERVICE.check(); + } + @SuppressWarnings({ "squid:S3776", "squid:S106" @@ -133,6 +154,8 @@ public static void main(String[] args) throws IOException { } } else if ((header.getChunkType() & TsFileConstant.VALUE_COLUMN_MASK) == TsFileConstant.VALUE_COLUMN_MASK) { // Value Chunk + PageValueReader valueReader = + PAGE_VALUE_READER_SERVICE.call(Type.fromTsDataType(header.getDataType())); int pointNum = 0; byte[] bitmap = null; if (pageData.hasRemaining()) { @@ -149,33 +172,7 @@ public static void main(String[] args) throws IOException { } continue; } - Object value; - switch (header.getDataType()) { - case BOOLEAN: - value = valueDecoder.readBoolean(pageData); - break; - case INT32: - case DATE: - value = valueDecoder.readInt(pageData); - break; - case INT64: - case TIMESTAMP: - value = valueDecoder.readLong(pageData); - break; - case FLOAT: - value = valueDecoder.readFloat(pageData); - break; - case DOUBLE: - value = valueDecoder.readDouble(pageData); - break; - case TEXT: - case STRING: - case BLOB: - value = valueDecoder.readBinary(pageData); - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(header.getDataType())); - } + Object value = valueReader.read(valueDecoder, pageData); if (printDetail) { System.out.println("\t\t\tvalue: " + value); } @@ -240,4 +237,9 @@ public static void main(String[] args) throws IOException { } } } + + @FunctionalInterface + private interface PageValueReader { + Object read(Decoder decoder, ByteBuffer buffer); + } } diff --git a/java/pom.xml b/java/pom.xml index 3106d99bb..00a18d68d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -188,7 +188,7 @@ org.apache.tsfile,,javax,java,\# - + UNIX diff --git a/java/tools/src/main/java/org/apache/tsfile/tools/ValueConverter.java b/java/tools/src/main/java/org/apache/tsfile/tools/ValueConverter.java index a45de52f9..75145f5e0 100644 --- a/java/tools/src/main/java/org/apache/tsfile/tools/ValueConverter.java +++ b/java/tools/src/main/java/org/apache/tsfile/tools/ValueConverter.java @@ -19,6 +19,8 @@ package org.apache.tsfile.tools; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import java.nio.charset.StandardCharsets; @@ -30,6 +32,102 @@ public class ValueConverter { + private static final TypeService FROM_STRING_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> (value, isMeasurement, timePrecision) -> Boolean.valueOf(value); + case INT32 -> (value, isMeasurement, timePrecision) -> Integer.valueOf(value); + case INT64 -> (value, isMeasurement, timePrecision) -> Long.valueOf(value); + case FLOAT -> (value, isMeasurement, timePrecision) -> Float.valueOf(value); + case DOUBLE -> (value, isMeasurement, timePrecision) -> Double.valueOf(value); + case TEXT, STRING -> + (value, isMeasurement, timePrecision) -> + isMeasurement ? new Binary(value, StandardCharsets.UTF_8) : value; + case BLOB -> + (value, isMeasurement, timePrecision) -> + new Binary(value.getBytes(StandardCharsets.UTF_8)); + case DATE -> (value, isMeasurement, timePrecision) -> parseDate(value); + case TIMESTAMP -> + (value, isMeasurement, timePrecision) -> parseTimestamp(value, timePrecision); + case OBJECT, ROW, UNKNOWN, VECTOR -> (value, isMeasurement, timePrecision) -> value; + }; + + private static final TypeService FROM_OBJECT_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (value, isMeasurement, timePrecision) -> + value instanceof Boolean ? value : Boolean.valueOf(value.toString()); + case INT32 -> + (value, isMeasurement, timePrecision) -> { + if (value instanceof Integer) { + return value; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return Integer.valueOf(value.toString()); + }; + case INT64 -> + (value, isMeasurement, timePrecision) -> { + if (value instanceof Long) { + return value; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return Long.valueOf(value.toString()); + }; + case FLOAT -> + (value, isMeasurement, timePrecision) -> { + if (value instanceof Float) { + return value; + } + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + return Float.valueOf(value.toString()); + }; + case DOUBLE -> + (value, isMeasurement, timePrecision) -> { + if (value instanceof Double) { + return value; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + return Double.valueOf(value.toString()); + }; + case TEXT, STRING -> + (value, isMeasurement, timePrecision) -> { + if (isMeasurement) { + return value instanceof Binary + ? value + : new Binary(value.toString(), StandardCharsets.UTF_8); + } + return value instanceof String ? value : value.toString(); + }; + case BLOB -> + (value, isMeasurement, timePrecision) -> { + if (value instanceof Binary) { + return value; + } + if (value instanceof byte[]) { + return new Binary((byte[]) value); + } + return new Binary(value.toString().getBytes(StandardCharsets.UTF_8)); + }; + case DATE -> (value, isMeasurement, timePrecision) -> toLocalDate(value); + case TIMESTAMP -> + (value, isMeasurement, timePrecision) -> toEpochLong(value, timePrecision); + case OBJECT, ROW, UNKNOWN, VECTOR -> (value, isMeasurement, timePrecision) -> value; + }; + + static { + FROM_STRING_SERVICE.check(); + FROM_OBJECT_SERVICE.check(); + } + public static Object convert(Object value, TSDataType targetType, boolean isMeasurement) { return convert(value, targetType, isMeasurement, "ms"); } @@ -47,101 +145,16 @@ public static Object convert( private static Object fromString( String value, TSDataType targetType, boolean isMeasurement, String timePrecision) { - switch (targetType) { - case BOOLEAN: - return Boolean.valueOf(value); - case INT32: - return Integer.valueOf(value); - case INT64: - return Long.valueOf(value); - case FLOAT: - return Float.valueOf(value); - case DOUBLE: - return Double.valueOf(value); - case TEXT: - case STRING: - if (isMeasurement) { - return new Binary(value, StandardCharsets.UTF_8); - } - return value; - case BLOB: - return new Binary(value.getBytes(StandardCharsets.UTF_8)); - case DATE: - return parseDate(value); - case TIMESTAMP: - return parseTimestamp(value, timePrecision); - default: - return value; - } + return FROM_STRING_SERVICE + .call(Type.fromTsDataType(targetType)) + .convert(value, isMeasurement, timePrecision); } private static Object fromObject( Object value, TSDataType targetType, boolean isMeasurement, String timePrecision) { - switch (targetType) { - case BOOLEAN: - if (value instanceof Boolean) { - return value; - } - return Boolean.valueOf(value.toString()); - case INT32: - if (value instanceof Integer) { - return value; - } - if (value instanceof Number) { - return ((Number) value).intValue(); - } - return Integer.valueOf(value.toString()); - case INT64: - if (value instanceof Long) { - return value; - } - if (value instanceof Number) { - return ((Number) value).longValue(); - } - return Long.valueOf(value.toString()); - case FLOAT: - if (value instanceof Float) { - return value; - } - if (value instanceof Number) { - return ((Number) value).floatValue(); - } - return Float.valueOf(value.toString()); - case DOUBLE: - if (value instanceof Double) { - return value; - } - if (value instanceof Number) { - return ((Number) value).doubleValue(); - } - return Double.valueOf(value.toString()); - case TEXT: - case STRING: - if (isMeasurement) { - if (value instanceof Binary) { - return value; - } - return new Binary(value.toString(), StandardCharsets.UTF_8); - } - if (value instanceof String) { - return value; - } - return value.toString(); - case BLOB: - if (value instanceof Binary) { - return value; - } - if (value instanceof byte[]) { - return new Binary((byte[]) value); - } - return new Binary(value.toString().getBytes(StandardCharsets.UTF_8)); - case DATE: - return toLocalDate(value); - case TIMESTAMP: - return toEpochLong(value, timePrecision); - default: - return value; - } + return FROM_OBJECT_SERVICE + .call(Type.fromTsDataType(targetType)) + .convert(value, isMeasurement, timePrecision); } private static LocalDate parseDate(String value) { @@ -220,4 +233,14 @@ private static long toEpochLong(Object value, String timePrecision) { } return parseTimestamp(value.toString(), timePrecision); } + + @FunctionalInterface + private interface StringConverter { + Object convert(String value, boolean isMeasurement, String timePrecision); + } + + @FunctionalInterface + private interface ObjectConverter { + Object convert(Object value, boolean isMeasurement, String timePrecision); + } } diff --git a/java/tsfile/pom.xml b/java/tsfile/pom.xml index ec327381c..768d23f95 100644 --- a/java/tsfile/pom.xml +++ b/java/tsfile/pom.xml @@ -145,10 +145,10 @@ shade - + false - + @@ -185,7 +185,7 @@ org.apache.tsfile.* common;inline=true false - + <_removeheaders>Bnd-LastModified,Built-By org.apache.tsfile diff --git a/java/tsfile/src/main/java/org/apache/tsfile/common/conf/TSFileConfig.java b/java/tsfile/src/main/java/org/apache/tsfile/common/conf/TSFileConfig.java index 764eda5bd..6c79d6567 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/common/conf/TSFileConfig.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/common/conf/TSFileConfig.java @@ -24,6 +24,7 @@ import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.fileSystem.FSType; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.FSUtils; import java.io.Serializable; @@ -370,62 +371,11 @@ public String getValueEncoder() { } public TSEncoding getValueEncoder(TSDataType dataType) { - switch (dataType) { - case BOOLEAN: - return booleanEncoding; - case INT32: - case DATE: - return int32Encoding; - case INT64: - case TIMESTAMP: - return int64Encoding; - case FLOAT: - return floatEncoding; - case DOUBLE: - return doubleEncoding; - case STRING: - case BLOB: - case TEXT: - case OBJECT: - default: - return textEncoding; - } + return Type.fromTsDataType(dataType).getDefaultEncoding(this); } public CompressionType getCompressor(TSDataType dataType) { - CompressionType compressionType; - switch (dataType) { - case BOOLEAN: - compressionType = booleanCompression; - break; - case INT32: - case DATE: - compressionType = int32Compression; - break; - case INT64: - case TIMESTAMP: - compressionType = int64Compression; - break; - case FLOAT: - compressionType = floatCompression; - break; - case DOUBLE: - compressionType = doubleCompression; - break; - case STRING: - case BLOB: - case TEXT: - case OBJECT: - compressionType = textCompression; - break; - default: - compressionType = null; - } - - if (compressionType == null) { - compressionType = compressor; - } - return compressionType; + return Type.fromTsDataType(dataType).getDefaultCompressor(this); } public void setValueEncoder(String valueEncoder) { @@ -532,6 +482,34 @@ public CompressionType getCompressor() { return compressor; } + public CompressionType getBooleanCompressor() { + return getCompressorOrDefault(booleanCompression); + } + + public CompressionType getInt32Compressor() { + return getCompressorOrDefault(int32Compression); + } + + public CompressionType getInt64Compressor() { + return getCompressorOrDefault(int64Compression); + } + + public CompressionType getFloatCompressor() { + return getCompressorOrDefault(floatCompression); + } + + public CompressionType getDoubleCompressor() { + return getCompressorOrDefault(doubleCompression); + } + + public CompressionType getTextCompressor() { + return getCompressorOrDefault(textCompression); + } + + private CompressionType getCompressorOrDefault(CompressionType compressionType) { + return compressionType == null ? compressor : compressionType; + } + public void setCompressor(String compressor) { this.compressor = CompressionType.valueOf(compressor); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encoding/decoder/Decoder.java b/java/tsfile/src/main/java/org/apache/tsfile/encoding/decoder/Decoder.java index 340316bb1..bfa1218ab 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encoding/decoder/Decoder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encoding/decoder/Decoder.java @@ -23,6 +23,7 @@ import org.apache.tsfile.exception.encoding.TsFileDecodingException; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import java.io.IOException; @@ -31,8 +32,6 @@ public abstract class Decoder { - private static final String ERROR_MSG_KEY = "error.encoding.decoder_not_found"; - private TSEncoding type; public Decoder(TSEncoding type) { @@ -48,146 +47,7 @@ public TSEncoding getType() { } public static Decoder getDecoderByType(TSEncoding encoding, TSDataType dataType) { - switch (encoding) { - case PLAIN: - return new PlainDecoder(); - case RLE: - switch (dataType) { - case BOOLEAN: - case INT32: - case DATE: - return new IntRleDecoder(); - case INT64: - case VECTOR: - case TIMESTAMP: - return new LongRleDecoder(); - case FLOAT: - case DOUBLE: - return new FloatDecoder(TSEncoding.valueOf(encoding.toString()), dataType); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case TS_2DIFF: - switch (dataType) { - case INT32: - case DATE: - return new DeltaBinaryDecoder.IntDeltaDecoder(); - case INT64: - case VECTOR: - case TIMESTAMP: - return new DeltaBinaryDecoder.LongDeltaDecoder(); - case FLOAT: - case DOUBLE: - return new FloatDecoder(TSEncoding.valueOf(encoding.toString()), dataType); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case GORILLA_V1: - switch (dataType) { - case FLOAT: - return new SinglePrecisionDecoderV1(); - case DOUBLE: - return new DoublePrecisionDecoderV1(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case REGULAR: - switch (dataType) { - case INT32: - case DATE: - return new RegularDataDecoder.IntRegularDecoder(); - case INT64: - case VECTOR: - case TIMESTAMP: - return new RegularDataDecoder.LongRegularDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case GORILLA: - switch (dataType) { - case FLOAT: - return new SinglePrecisionDecoderV2(); - case DOUBLE: - return new DoublePrecisionDecoderV2(); - case INT32: - case DATE: - return new IntGorillaDecoder(); - case INT64: - case VECTOR: - case TIMESTAMP: - return new LongGorillaDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case DICTIONARY: - return new DictionaryDecoder(); - case ZIGZAG: - switch (dataType) { - case INT32: - case DATE: - return new IntZigzagDecoder(); - case INT64: - case TIMESTAMP: - return new LongZigzagDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case CHIMP: - switch (dataType) { - case FLOAT: - return new SinglePrecisionChimpDecoder(); - case DOUBLE: - return new DoublePrecisionChimpDecoder(); - case INT32: - case DATE: - return new IntChimpDecoder(); - case INT64: - case VECTOR: - case TIMESTAMP: - return new LongChimpDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case SPRINTZ: - switch (dataType) { - case INT32: - case DATE: - return new IntSprintzDecoder(); - case INT64: - case TIMESTAMP: - return new LongSprintzDecoder(); - case FLOAT: - return new FloatSprintzDecoder(); - case DOUBLE: - return new DoubleSprintzDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case RLBE: - switch (dataType) { - case INT32: - case DATE: - return new IntRLBEDecoder(); - case INT64: - case TIMESTAMP: - return new LongRLBEDecoder(); - case FLOAT: - return new FloatRLBEDecoder(); - case DOUBLE: - return new DoubleRLBEDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - case CAMEL: - switch (dataType) { - case DOUBLE: - return new CamelDecoder(); - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } - default: - throw new TsFileDecodingException(Messages.format(ERROR_MSG_KEY, encoding, dataType)); - } + return Type.fromTsDataType(dataType).getDecoder(encoding); } public int readInt(ByteBuffer buffer) { diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/PlainEncoder.java b/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/PlainEncoder.java index 0f9fd8206..f2b518c61 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/PlainEncoder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/PlainEncoder.java @@ -19,11 +19,11 @@ package org.apache.tsfile.encoding.encoder; -import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.encoding.TsFileEncodingException; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.ReadWriteForEncodingUtils; @@ -112,28 +112,7 @@ public void flush(ByteArrayOutputStream out) { @Override public int getOneItemMaxSize() { - switch (dataType) { - case BOOLEAN: - return 1; - case INT32: - case DATE: - return 4; - case INT64: - case TIMESTAMP: - return 8; - case FLOAT: - return 4; - case DOUBLE: - return 8; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - // refer to encode(Binary,ByteArrayOutputStream) - return 4 + TSFileConfig.BYTE_SIZE_PER_CHAR * maxStringLength; - default: - throw new UnsupportedOperationException(dataType.toString()); - } + return Type.fromTsDataType(dataType).getOneItemMaxSize(maxStringLength); } @Override diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/TSEncodingBuilder.java b/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/TSEncodingBuilder.java index 77d6568ee..0db1cc211 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/TSEncodingBuilder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encoding/encoder/TSEncodingBuilder.java @@ -25,6 +25,8 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.utils.EncodingTypeServices; import org.apache.tsfile.write.UnSupportedDataTypeException; import org.slf4j.Logger; @@ -143,21 +145,9 @@ public static class Rle extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - case BOOLEAN: - return new IntRleEncoder(); - case INT64: - case TIMESTAMP: - return new LongRleEncoder(); - case FLOAT: - case DOUBLE: - return new FloatEncoder(TSEncoding.RLE, type, maxPointNumber); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.RLE, type)); - } + return EncodingTypeServices.RLE_ENCODER_SERVICE + .call(Type.fromTsDataType(type)) + .build(maxPointNumber); } /** @@ -198,20 +188,9 @@ public static class Ts2Diff extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - return new DeltaBinaryEncoder.IntDeltaEncoder(); - case INT64: - case TIMESTAMP: - return new DeltaBinaryEncoder.LongDeltaEncoder(); - case FLOAT: - case DOUBLE: - return new FloatEncoder(TSEncoding.TS_2DIFF, type, maxPointNumber); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.TS_2DIFF, type)); - } + return EncodingTypeServices.TS_2DIFF_ENCODER_SERVICE + .call(Type.fromTsDataType(type)) + .build(maxPointNumber); } /** @@ -250,15 +229,7 @@ public static class GorillaV1 extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case FLOAT: - return new SinglePrecisionEncoderV1(); - case DOUBLE: - return new DoublePrecisionEncoderV1(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.GORILLA_V1, type)); - } + return EncodingTypeServices.GORILLA_V1_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -290,17 +261,7 @@ public static class Regular extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - return new RegularDataEncoder.IntRegularEncoder(); - case INT64: - case TIMESTAMP: - return new RegularDataEncoder.LongRegularEncoder(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.REGULAR, type)); - } + return EncodingTypeServices.REGULAR_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -314,21 +275,7 @@ public static class GorillaV2 extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case FLOAT: - return new SinglePrecisionEncoderV2(); - case DOUBLE: - return new DoublePrecisionEncoderV2(); - case INT32: - case DATE: - return new IntGorillaEncoder(); - case INT64: - case TIMESTAMP: - return new LongGorillaEncoder(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.GORILLA, type)); - } + return EncodingTypeServices.GORILLA_V2_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -340,21 +287,7 @@ public void initFromProps(Map props) { public static class Sprintz extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - return new IntSprintzEncoder(); - case INT64: - case TIMESTAMP: - return new LongSprintzEncoder(); - case FLOAT: - return new FloatSprintzEncoder(); - case DOUBLE: - return new DoubleSprintzEncoder(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.SPRINTZ, type)); - } + return EncodingTypeServices.SPRINTZ_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -367,21 +300,7 @@ public static class RLBE extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - return new IntRLBE(); - case INT64: - case TIMESTAMP: - return new LongRLBE(); - case FLOAT: - return new FloatRLBE(); - case DOUBLE: - return new DoubleRLBE(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.RLBE, type)); - } + return EncodingTypeServices.RLBE_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -411,17 +330,7 @@ public static class Zigzag extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case INT32: - case DATE: - return new IntZigzagEncoder(); - case INT64: - case TIMESTAMP: - return new LongZigzagEncoder(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.ZIGZAG, type)); - } + return EncodingTypeServices.ZIGZAG_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override @@ -435,21 +344,7 @@ public static class Chimp extends TSEncodingBuilder { @Override public Encoder getEncoder(TSDataType type) { - switch (type) { - case FLOAT: - return new SinglePrecisionChimpEncoder(); - case DOUBLE: - return new DoublePrecisionChimpEncoder(); - case INT32: - case DATE: - return new IntChimpEncoder(); - case INT64: - case TIMESTAMP: - return new LongChimpEncoder(); - default: - throw new UnSupportedDataTypeException( - Messages.format(ERROR_MSG_KEY, TSEncoding.CHIMP, type)); - } + return EncodingTypeServices.CHIMP_ENCODER_SERVICE.call(Type.fromTsDataType(type)); } @Override diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java index 323115aee..2b9ff6f29 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java @@ -23,6 +23,7 @@ import org.apache.tsfile.exception.filter.StatisticsClassException; import org.apache.tsfile.exception.write.UnknownColumnTypeException; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.Pair; @@ -104,65 +105,11 @@ public abstract class Statistics { * @throws UnknownColumnTypeException if the type is unknown */ public static Statistics getStatsByType(TSDataType type) { - switch (type) { - case INT32: - return new IntegerStatistics(); - case INT64: - return new LongStatistics(); - case TEXT: - return new BinaryStatistics(); - case BOOLEAN: - return new BooleanStatistics(); - case DOUBLE: - return new DoubleStatistics(); - case FLOAT: - return new FloatStatistics(); - case VECTOR: - return new TimeStatistics(); - case DATE: - return new DateStatistics(); - case TIMESTAMP: - return new TimestampStatistics(); - case STRING: - return new StringStatistics(); - case BLOB: - return new BlobStatistics(); - case OBJECT: - return new ObjectStatistics(); - default: - throw new UnknownColumnTypeException(type.toString()); - } + return Type.fromTsDataType(type).createStatistics(); } public static long getSizeByType(TSDataType type) { - switch (type) { - case INT32: - return IntegerStatistics.INSTANCE_SIZE; - case INT64: - return LongStatistics.INSTANCE_SIZE; - case TEXT: - return BinaryStatistics.INSTANCE_SIZE; - case BOOLEAN: - return BooleanStatistics.INSTANCE_SIZE; - case DOUBLE: - return DoubleStatistics.INSTANCE_SIZE; - case FLOAT: - return FloatStatistics.INSTANCE_SIZE; - case VECTOR: - return TimeStatistics.INSTANCE_SIZE; - case DATE: - return DateStatistics.INSTANCE_SIZE; - case TIMESTAMP: - return TimestampStatistics.INSTANCE_SIZE; - case STRING: - return StringStatistics.INSTANCE_SIZE; - case BLOB: - return BlobStatistics.INSTANCE_SIZE; - case OBJECT: - return ObjectStatistics.INSTANCE_SIZE; - default: - throw new UnknownColumnTypeException(type.toString()); - } + return Type.fromTsDataType(type).getStatisticsInstanceSize(); } public abstract TSDataType getType(); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java index 97dcddc0d..396a75576 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java @@ -66,6 +66,7 @@ import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.Chunk; import org.apache.tsfile.read.common.Path; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.controller.CachedChunkLoaderImpl; import org.apache.tsfile.read.controller.MetadataQuerierByFileImpl; import org.apache.tsfile.read.reader.TsFileInput; @@ -2457,6 +2458,7 @@ public long selfCheck( chunkHeader.getCompressionType()); measurementSchemaList.add(measurementSchema); dataType = chunkHeader.getDataType(); + Type valueType = Type.fromTsDataType(dataType); Statistics chunkStatistics = Statistics.getStatsByType(dataType); @@ -2534,34 +2536,7 @@ public long selfCheck( continue; } long timeStamp = timeBatch.get(timeBatchIndex)[i]; - switch (dataType) { - case INT32: - case DATE: - chunkStatistics.update(timeStamp, value.getInt()); - break; - case INT64: - case TIMESTAMP: - chunkStatistics.update(timeStamp, value.getLong()); - break; - case FLOAT: - chunkStatistics.update(timeStamp, value.getFloat()); - break; - case DOUBLE: - chunkStatistics.update(timeStamp, value.getDouble()); - break; - case BOOLEAN: - chunkStatistics.update(timeStamp, value.getBoolean()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - chunkStatistics.update(timeStamp, value.getBinary()); - break; - default: - throw new IOException( - Messages.format("error.read.unexpected_type", dataType)); - } + valueType.update(chunkStatistics, timeStamp, value); } } @@ -2575,34 +2550,7 @@ public long selfCheck( timeDecoder); BatchData batchData = reader.getAllSatisfiedPageData(); while (batchData.hasCurrent()) { - switch (dataType) { - case INT32: - case DATE: - chunkStatistics.update(batchData.currentTime(), batchData.getInt()); - break; - case INT64: - case TIMESTAMP: - chunkStatistics.update(batchData.currentTime(), batchData.getLong()); - break; - case FLOAT: - chunkStatistics.update(batchData.currentTime(), batchData.getFloat()); - break; - case DOUBLE: - chunkStatistics.update(batchData.currentTime(), batchData.getDouble()); - break; - case BOOLEAN: - chunkStatistics.update(batchData.currentTime(), batchData.getBoolean()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - chunkStatistics.update(batchData.currentTime(), batchData.getBinary()); - break; - default: - throw new IOException( - Messages.format("error.read.unexpected_type", dataType)); - } + valueType.update(chunkStatistics, batchData); batchData.next(); } } @@ -2781,6 +2729,7 @@ public long checkChunkAndPagesStatistics(IChunkMetadata chunkMetadata) throws IO byte marker = this.readMarker(); ChunkHeader chunkHeader = this.readChunkHeader(marker); TSDataType dataType = chunkHeader.getDataType(); + Type valueType = Type.fromTsDataType(dataType); Statistics chunkStatistics = Statistics.getStatsByType(dataType); int dataSize = chunkHeader.getDataSize(); if (((byte) (chunkHeader.getChunkType() & 0x3F)) == MetaMarker.CHUNK_HEADER) { @@ -2808,33 +2757,7 @@ public long checkChunkAndPagesStatistics(IChunkMetadata chunkMetadata) throws IO pageHeader, pageData, chunkHeader.getDataType(), valueDecoder, timeDecoder); BatchData batchData = reader.getAllSatisfiedPageData(); while (batchData.hasCurrent()) { - switch (dataType) { - case INT32: - case DATE: - chunkStatistics.update(batchData.currentTime(), batchData.getInt()); - break; - case INT64: - case TIMESTAMP: - chunkStatistics.update(batchData.currentTime(), batchData.getLong()); - break; - case FLOAT: - chunkStatistics.update(batchData.currentTime(), batchData.getFloat()); - break; - case DOUBLE: - chunkStatistics.update(batchData.currentTime(), batchData.getDouble()); - break; - case BOOLEAN: - chunkStatistics.update(batchData.currentTime(), batchData.getBoolean()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - chunkStatistics.update(batchData.currentTime(), batchData.getBinary()); - break; - default: - throw new IOException(Messages.format("error.read.unexpected_type", dataType)); - } + valueType.update(chunkStatistics, batchData); batchData.next(); } chunkHeader.increasePageNums(1); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/BatchData.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/BatchData.java index 9fbda97f1..7633a62fd 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/BatchData.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/BatchData.java @@ -23,17 +23,10 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.TimeValuePair; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.IPointReader; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.TsPrimitiveType; -import org.apache.tsfile.utils.TsPrimitiveType.TsBinary; -import org.apache.tsfile.utils.TsPrimitiveType.TsBoolean; -import org.apache.tsfile.utils.TsPrimitiveType.TsDouble; -import org.apache.tsfile.utils.TsPrimitiveType.TsFloat; -import org.apache.tsfile.utils.TsPrimitiveType.TsInt; -import org.apache.tsfile.utils.TsPrimitiveType.TsLong; -import org.apache.tsfile.utils.TsPrimitiveType.TsVector; -import org.apache.tsfile.write.UnSupportedDataTypeException; import java.io.DataOutputStream; import java.io.IOException; @@ -129,55 +122,12 @@ public long currentTime() { * @return current value */ public Object currentValue() { - switch (dataType) { - case INT32: - case DATE: - return getInt(); - case INT64: - case TIMESTAMP: - return getLong(); - case FLOAT: - return getFloat(); - case DOUBLE: - return getDouble(); - case BOOLEAN: - return getBoolean(); - case TEXT: - case BLOB: - case STRING: - case OBJECT: - return getBinary(); - case VECTOR: - return getVector(); - default: - return null; - } + return Type.fromTsDataType(dataType).getCurrentValue(this); } public TsPrimitiveType currentTsPrimitiveType() { - switch (dataType) { - case INT32: - case DATE: - return new TsInt(getInt()); - case INT64: - case TIMESTAMP: - return new TsLong(getLong()); - case FLOAT: - return new TsFloat(getFloat()); - case DOUBLE: - return new TsDouble(getDouble()); - case BOOLEAN: - return new TsBoolean(getBoolean()); - case TEXT: - case BLOB: - case STRING: - case OBJECT: - return new TsBinary(getBinary()); - case VECTOR: - return new TsVector(getVector()); - default: - return null; - } + Type type = Type.fromTsDataType(dataType); + return type.getTsPrimitiveType(type.getCurrentValue(this)); } public TSDataType getDataType() { @@ -208,43 +158,42 @@ public void init(TSDataType type) { timeRet.add(new long[capacity]); count = 0; - switch (dataType) { - case BOOLEAN: - booleanRet = new ArrayList<>(); - booleanRet.add(new boolean[capacity]); - break; - case INT32: - case DATE: - intRet = new ArrayList<>(); - intRet.add(new int[capacity]); - break; - case INT64: - case TIMESTAMP: - longRet = new ArrayList<>(); - longRet.add(new long[capacity]); - break; - case FLOAT: - floatRet = new ArrayList<>(); - floatRet.add(new float[capacity]); - break; - case DOUBLE: - doubleRet = new ArrayList<>(); - doubleRet.add(new double[capacity]); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - binaryRet = new ArrayList<>(); - binaryRet.add(new Binary[capacity]); - break; - case VECTOR: - vectorRet = new ArrayList<>(); - vectorRet.add(new TsPrimitiveType[capacity][]); - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + Type.fromTsDataType(dataType).init(this); + } + + public void initBooleanValues() { + booleanRet = new ArrayList<>(); + booleanRet.add(new boolean[capacity]); + } + + public void initIntValues() { + intRet = new ArrayList<>(); + intRet.add(new int[capacity]); + } + + public void initLongValues() { + longRet = new ArrayList<>(); + longRet.add(new long[capacity]); + } + + public void initFloatValues() { + floatRet = new ArrayList<>(); + floatRet.add(new float[capacity]); + } + + public void initDoubleValues() { + doubleRet = new ArrayList<>(); + doubleRet.add(new double[capacity]); + } + + public void initBinaryValues() { + binaryRet = new ArrayList<>(); + binaryRet.add(new Binary[capacity]); + } + + public void initVectorValues() { + vectorRet = new ArrayList<>(); + vectorRet.add(new TsPrimitiveType[capacity][]); } /** @@ -558,36 +507,7 @@ public void setTime(long v) { * @param v object */ public void putAnObject(long t, Object v) { - switch (dataType) { - case BOOLEAN: - putBoolean(t, (boolean) v); - break; - case INT32: - case DATE: - putInt(t, (int) v); - break; - case INT64: - case TIMESTAMP: - putLong(t, (long) v); - break; - case FLOAT: - putFloat(t, (float) v); - break; - case DOUBLE: - putDouble(t, (double) v); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - putBinary(t, (Binary) v); - break; - case VECTOR: - putVector(t, (TsPrimitiveType[]) v); - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + Type.fromTsDataType(dataType).put(this, t, v); } public int length() { @@ -682,99 +602,7 @@ public IBatchDataIterator getBatchDataIterator(int subIndex) { * writing, otherwise after deserializing the sequence will be reversed */ public void serializeData(DataOutputStream outputStream) throws IOException { - switch (dataType) { - case BOOLEAN: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeBoolean(getBooleanByIndex(i)); - } - break; - case DOUBLE: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeDouble(getDoubleByIndex(i)); - } - break; - case FLOAT: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeFloat(getFloatByIndex(i)); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - Binary binary = getBinaryByIndex(i); - outputStream.writeInt(binary.getLength()); - outputStream.write(binary.getValues()); - } - break; - case INT64: - case TIMESTAMP: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeLong(getLongByIndex(i)); - } - break; - case INT32: - case DATE: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeInt(getIntByIndex(i)); - } - break; - case VECTOR: - for (int i = 0; i < length(); i++) { - outputStream.writeLong(getTimeByIndex(i)); - TsPrimitiveType[] values = getVectorByIndex(i); - outputStream.writeInt(values.length); - for (TsPrimitiveType value : values) { - if (value == null) { - outputStream.write(0); - } else { - outputStream.write(1); - outputStream.write(value.getDataType().serialize()); - switch (value.getDataType()) { - case BOOLEAN: - outputStream.writeBoolean(value.getBoolean()); - break; - case DOUBLE: - outputStream.writeDouble(value.getDouble()); - break; - case FLOAT: - outputStream.writeFloat(value.getFloat()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary binary = value.getBinary(); - outputStream.writeInt(binary.getLength()); - outputStream.write(binary.getValues()); - break; - case INT64: - case TIMESTAMP: - outputStream.writeLong(value.getLong()); - break; - case INT32: - case DATE: - outputStream.writeInt(value.getInt()); - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.batch_data_unknown_type", dataType)); - } - } - } - } - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.batch_data_unknown_type", dataType)); - } + Type.fromTsDataType(dataType).serialize(this, outputStream, false); } /** diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/Chunk.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/Chunk.java index bf83a4539..d41ad14a3 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/Chunk.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/Chunk.java @@ -29,11 +29,11 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.TimeValuePair; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.IPageReader; import org.apache.tsfile.read.reader.IPointReader; import org.apache.tsfile.read.reader.chunk.ChunkReader; import org.apache.tsfile.read.reader.chunk.TableChunkReader; -import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.PublicBAOS; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteForEncodingUtils; @@ -257,51 +257,8 @@ public Chunk rewrite(TSDataType newType, Chunk timeChunk) throws IOException { chunkHeader.getDataType(), point.getValue().getVector()[0].getValue()); } long timestamp = point.getTimestamp(); - switch (newType) { - case BOOLEAN: - chunkWriter.write( - timestamp, - convertedValue == null ? true : (boolean) convertedValue, - convertedValue == null); - break; - case DATE: - case INT32: - chunkWriter.write( - timestamp, - convertedValue == null ? Integer.MAX_VALUE : (int) convertedValue, - convertedValue == null); - break; - case TIMESTAMP: - case INT64: - chunkWriter.write( - timestamp, - convertedValue == null ? (long) Integer.MAX_VALUE : (long) convertedValue, - convertedValue == null); - break; - case FLOAT: - chunkWriter.write( - timestamp, - convertedValue == null ? (float) Integer.MAX_VALUE : (float) convertedValue, - convertedValue == null); - break; - case DOUBLE: - chunkWriter.write( - timestamp, - convertedValue == null ? (double) Integer.MAX_VALUE : (double) convertedValue, - convertedValue == null); - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - chunkWriter.write( - timestamp, - convertedValue == null ? Binary.EMPTY_VALUE : (Binary) convertedValue, - convertedValue == null); - break; - default: - throw new IOException(Messages.format("error.read.chunk_unsupported_type", newType)); - } + Type.fromTsDataType(newType) + .write(chunkWriter, timestamp, convertedValue, convertedValue == null); } chunkWriter.sealCurrentPage(); } @@ -346,33 +303,7 @@ public Chunk rewrite(TSDataType newType) throws IOException { if (convertedValue == null) { throw new IOException(Messages.format("error.read.chunk_non_aligned_null", timestamp)); } - switch (newType) { - case BOOLEAN: - chunkWriter.write(timestamp, (boolean) convertedValue); - break; - case DATE: - case INT32: - chunkWriter.write(timestamp, (int) convertedValue); - break; - case TIMESTAMP: - case INT64: - chunkWriter.write(timestamp, (long) convertedValue); - break; - case FLOAT: - chunkWriter.write(timestamp, (float) convertedValue); - break; - case DOUBLE: - chunkWriter.write(timestamp, (double) convertedValue); - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - chunkWriter.write(timestamp, (Binary) convertedValue); - break; - default: - throw new IOException(Messages.format("error.read.chunk_unsupported_type", newType)); - } + Type.fromTsDataType(newType).write(chunkWriter, timestamp, convertedValue); } chunkWriter.sealCurrentPage(); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/DescReadWriteBatchData.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/DescReadWriteBatchData.java index b667c537d..78ec4ed3c 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/DescReadWriteBatchData.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/DescReadWriteBatchData.java @@ -20,9 +20,9 @@ package org.apache.tsfile.read.common; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.TsPrimitiveType; -import org.apache.tsfile.write.UnSupportedDataTypeException; import java.io.DataOutputStream; import java.io.IOException; @@ -49,43 +49,7 @@ public DescReadWriteBatchData(TSDataType dataType) { timeRet.add(new long[capacity]); count = 0; - switch (dataType) { - case BOOLEAN: - booleanRet = new LinkedList<>(); - booleanRet.add(new boolean[capacity]); - break; - case INT32: - case DATE: - intRet = new LinkedList<>(); - intRet.add(new int[capacity]); - break; - case INT64: - case TIMESTAMP: - longRet = new LinkedList<>(); - longRet.add(new long[capacity]); - break; - case FLOAT: - floatRet = new LinkedList<>(); - floatRet.add(new float[capacity]); - break; - case DOUBLE: - doubleRet = new LinkedList<>(); - doubleRet.add(new double[capacity]); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - binaryRet = new LinkedList<>(); - binaryRet.add(new Binary[capacity]); - break; - case VECTOR: - vectorRet = new LinkedList<>(); - vectorRet.add(new TsPrimitiveType[capacity][]); - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + Type.fromTsDataType(dataType).init(this); } /** @@ -419,97 +383,7 @@ public TsPrimitiveType[] getVectorByIndex(int idx) { @Override public void serializeData(DataOutputStream outputStream) throws IOException { - switch (dataType) { - case BOOLEAN: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeBoolean(getBooleanByIndex(i)); - } - break; - case DOUBLE: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeDouble(getDoubleByIndex(i)); - } - break; - case FLOAT: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeFloat(getFloatByIndex(i)); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - Binary binary = getBinaryByIndex(i); - outputStream.writeInt(binary.getLength()); - outputStream.write(binary.getValues()); - } - break; - case INT64: - case TIMESTAMP: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeLong(getLongByIndex(i)); - } - break; - case INT32: - case DATE: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - outputStream.writeInt(getIntByIndex(i)); - } - break; - case VECTOR: - for (int i = length() - 1; i >= 0; i--) { - outputStream.writeLong(getTimeByIndex(i)); - TsPrimitiveType[] values = getVectorByIndex(i); - outputStream.writeInt(values.length); - for (TsPrimitiveType value : values) { - if (value == null) { - outputStream.write(0); - } else { - outputStream.write(1); - outputStream.write(value.getDataType().serialize()); - switch (value.getDataType()) { - case BOOLEAN: - outputStream.writeBoolean(value.getBoolean()); - break; - case DOUBLE: - outputStream.writeDouble(value.getDouble()); - break; - case FLOAT: - outputStream.writeFloat(value.getFloat()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary binary = value.getBinary(); - outputStream.writeInt(binary.getLength()); - outputStream.write(binary.getValues()); - break; - case INT64: - case TIMESTAMP: - outputStream.writeLong(value.getLong()); - break; - case INT32: - case DATE: - outputStream.writeInt(value.getInt()); - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } - } - } - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + Type.fromTsDataType(dataType).serialize(this, outputStream, true); } /** diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/Field.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/Field.java index 1e8a3afcc..21e192a07 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/Field.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/Field.java @@ -21,17 +21,13 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.NullFieldException; -import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; -import org.apache.tsfile.utils.BytesUtils; import org.apache.tsfile.utils.DateUtils; import org.apache.tsfile.utils.TsPrimitiveType; -import org.apache.tsfile.write.UnSupportedDataTypeException; import java.time.LocalDate; -import static org.apache.tsfile.utils.BytesUtils.parseObjectByteArrayToString; - /** * Field is component of one {@code RowRecord} which stores a value in specific data type. The value * type of Field is primitive(int long, float, double, binary, boolean). @@ -53,33 +49,7 @@ public Field(TSDataType dataType) { public static Field copy(Field field) { Field out = new Field(field.dataType); if (out.dataType != null) { - switch (out.dataType) { - case DOUBLE: - out.setDoubleV(field.getDoubleV()); - break; - case FLOAT: - out.setFloatV(field.getFloatV()); - break; - case INT64: - case TIMESTAMP: - out.setLongV(field.getLongV()); - break; - case INT32: - case DATE: - out.setIntV(field.getIntV()); - break; - case BOOLEAN: - out.setBoolV(field.getBoolV()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - out.setBinaryV(field.getBinaryV()); - break; - default: - throw new UnSupportedDataTypeException(out.dataType.toString()); - } + Type.fromTsDataType(out.dataType).setTo(field, out); } return out; @@ -147,9 +117,6 @@ public void setDoubleV(double doubleV) { public Binary getBinaryV() { if (dataType == null) { throw new NullFieldException(); - } else if (dataType == TSDataType.OBJECT) { - throw new UnsupportedOperationException( - Messages.get("error.read.field_object_type_no_binary")); } return binaryV; } @@ -174,29 +141,7 @@ public String getStringValue() { if (dataType == null) { return "null"; } - switch (dataType) { - case BOOLEAN: - return String.valueOf(boolV); - case INT32: - case DATE: - return String.valueOf(intV); - case INT64: - case TIMESTAMP: - return String.valueOf(longV); - case FLOAT: - return String.valueOf(floatV); - case DOUBLE: - return String.valueOf(doubleV); - case TEXT: - case STRING: - return binaryV.toString(); - case OBJECT: - return parseObjectByteArrayToString(binaryV.getValues()); - case BLOB: - return BytesUtils.parseBlobByteArrayToString(binaryV.getValues()); - default: - throw new UnSupportedDataTypeException(dataType.toString()); - } + return Type.fromTsDataType(dataType).toString(this); } @Override @@ -208,94 +153,17 @@ public Object getObjectValue(TSDataType dataType) { if (this.dataType == null) { return null; } - switch (dataType) { - case DOUBLE: - return getDoubleV(); - case FLOAT: - return getFloatV(); - case INT64: - case TIMESTAMP: - return getLongV(); - case INT32: - return getIntV(); - case DATE: - return getDateV(); - case BOOLEAN: - return getBoolV(); - case TEXT: - case BLOB: - case STRING: - return getBinaryV(); - case OBJECT: - return getStringValue(); - default: - throw new UnSupportedDataTypeException(dataType.toString()); - } + return Type.fromTsDataType(dataType).getValue(this); } public static Field getField(Object value, TSDataType dataType) { if (value == null) { return null; } - Field field = new Field(dataType); - switch (dataType) { - case INT32: - case DATE: - field.setIntV((int) value); - break; - case INT64: - case TIMESTAMP: - field.setLongV((long) value); - break; - case FLOAT: - field.setFloatV((float) value); - break; - case DOUBLE: - field.setDoubleV((double) value); - break; - case BOOLEAN: - field.setBoolV((boolean) value); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - field.setBinaryV((Binary) value); - break; - default: - throw new UnSupportedDataTypeException(dataType.toString()); - } - return field; + return Type.fromTsDataType(dataType).getField(value); } public static void setTsPrimitiveValue(TsPrimitiveType value, Field field) { - switch (value.getDataType()) { - case BOOLEAN: - field.setBoolV(value.getBoolean()); - break; - case INT32: - case DATE: - field.setIntV(value.getInt()); - break; - case INT64: - case TIMESTAMP: - field.setLongV(value.getLong()); - break; - case FLOAT: - field.setFloatV(value.getFloat()); - break; - case DOUBLE: - field.setDoubleV(value.getDouble()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - field.setBinaryV(value.getBinary()); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.common.unsupported_data_type", value.getDataType())); - } + Type.fromTsDataType(value.getDataType()).setTo(value, field); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlock.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlock.java index 658024c14..de6158409 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlock.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlock.java @@ -26,10 +26,10 @@ import org.apache.tsfile.read.common.IBatchDataIterator; import org.apache.tsfile.read.common.block.column.ColumnFactory; import org.apache.tsfile.read.common.block.column.TimeColumn; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.IPointReader; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; -import org.apache.tsfile.write.UnSupportedDataTypeException; import org.apache.tsfile.write.schema.IMeasurementSchema; import java.util.Arrays; @@ -560,47 +560,9 @@ public void updateWithoutTimeColumn(int updateIdx, TsBlock sourceTsBlock, int so valueColumns[i].isNull()[updateIdx] = true; continue; } - switch (valueColumns[i].getDataType()) { - case BOOLEAN: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getBooleans()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getBoolean(sourceIndex); - break; - case INT32: - case DATE: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getInts()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getInt(sourceIndex); - break; - case INT64: - case TIMESTAMP: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getLongs()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getLong(sourceIndex); - break; - case FLOAT: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getFloats()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getFloat(sourceIndex); - break; - case DOUBLE: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getDoubles()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getDouble(sourceIndex); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - valueColumns[i].isNull()[updateIdx] = false; - valueColumns[i].getBinaries()[updateIdx] = - sourceTsBlock.getValueColumns()[i].getBinary(sourceIndex); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format( - "error.read.tsblock_unknown_datatype", valueColumns[i].getDataType())); - } + valueColumns[i].isNull()[updateIdx] = false; + Type.fromTsDataType(valueColumns[i].getDataType()) + .setTo(sourceTsBlock.getValueColumns()[i], sourceIndex, valueColumns[i], updateIdx); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlockBuilder.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlockBuilder.java index 456167304..e8868d04b 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlockBuilder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/TsBlockBuilder.java @@ -26,14 +26,9 @@ import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; -import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; -import org.apache.tsfile.read.common.block.column.BooleanColumnBuilder; -import org.apache.tsfile.read.common.block.column.DoubleColumnBuilder; -import org.apache.tsfile.read.common.block.column.FloatColumnBuilder; -import org.apache.tsfile.read.common.block.column.IntColumnBuilder; -import org.apache.tsfile.read.common.block.column.LongColumnBuilder; import org.apache.tsfile.read.common.block.column.TimeColumn; import org.apache.tsfile.read.common.block.column.TimeColumnBuilder; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import java.util.List; @@ -104,54 +99,10 @@ private TsBlockBuilder(int initialExpectedEntries, int maxTsBlockBytes, List types) { valueColumnBuilders = new ColumnBuilder[types.size()]; int initialExpectedEntries = timeColumnBuilder.getPositionCount(); for (int i = 0; i < valueColumnBuilders.length; i++) { - switch (types.get(i)) { - case BOOLEAN: - valueColumnBuilders[i] = - new BooleanColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), initialExpectedEntries); - break; - case INT32: - valueColumnBuilders[i] = - new IntColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), - initialExpectedEntries, - TSDataType.INT32); - break; - case DATE: - valueColumnBuilders[i] = - new IntColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), - initialExpectedEntries, - TSDataType.DATE); - break; - case INT64: - case TIMESTAMP: - valueColumnBuilders[i] = - new LongColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), initialExpectedEntries); - break; - case FLOAT: - valueColumnBuilders[i] = - new FloatColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), initialExpectedEntries); - break; - case DOUBLE: - valueColumnBuilders[i] = - new DoubleColumnBuilder( - tsBlockBuilderStatus.createColumnBuilderStatus(), initialExpectedEntries); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - valueColumnBuilders[i] = - new BinaryColumnBuilder( + valueColumnBuilders[i] = + Type.fromTsDataType(types.get(i)) + .createColumnBuilder( tsBlockBuilderStatus.createColumnBuilderStatus(), initialExpectedEntries); - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.tsblock_builder_unknown_type", types.get(i))); - } } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumn.java index 88f010381..6b67f87fb 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Optional; @@ -60,7 +65,7 @@ public BinaryColumn(int positionCount, Optional valueIsNull, Binary[] this(0, positionCount, valueIsNull.orElse(null), values); } - BinaryColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, Binary[] values) { + public BinaryColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, Binary[] values) { if (arrayOffset < 0) { throw new IllegalArgumentException(Messages.get("error.read.col_array_offset_negative")); } @@ -123,6 +128,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.BINARY_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.TEXT) { + return this; + } + ColumnUtil.checkConversion(TSDataType.TEXT, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else { + builder.writeBinary(getBinary(position)); + } + } + return builder.build(); + } + @Override public Binary getBinary(int position) { return values[position + arrayOffset]; @@ -143,6 +166,40 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsBinary(getBinary(position)); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + Binary value = values[index + arrayOffset]; + buffer.putInt(value.getLength()); + buffer.put(value.getValues()); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + Binary value = values[index + arrayOffset]; + stream.writeInt(value.getLength()); + stream.write(value.getValues()); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + Binary value = values[i + arrayOffset]; + output.writeInt(value.getLength()); + output.write(value.getValues()); + } + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getBinary(thisPos).equals(that.getBinary(thatPos)); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BooleanColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BooleanColumn.java index 318c450f7..38c6a8720 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BooleanColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BooleanColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; @@ -91,6 +96,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.BYTE_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.BOOLEAN) { + return this; + } + ColumnUtil.checkConversion(TSDataType.BOOLEAN, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else { + builder.writeBoolean(getBoolean(position)); + } + } + return builder.build(); + } + @Override public boolean getBoolean(int position) { return values[position + arrayOffset]; @@ -125,6 +148,48 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsBoolean(getBoolean(position)); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.put(values[index + arrayOffset] ? (byte) 1 : (byte) 0); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeBoolean(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + int nonNullCount = 0; + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + nonNullCount++; + } + } + + byte[] packedValues = new byte[(nonNullCount + 7) / 8]; + int nonNullIndex = 0; + for (int i = 0; i < positionCount; i++) { + if (isNull(i)) { + continue; + } + if (values[i + arrayOffset]) { + packedValues[nonNullIndex / 8] |= (byte) (0b1000_0000 >>> (nonNullIndex % 8)); + } + nonNullIndex++; + } + output.write(packedValues); + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getBoolean(thisPos) == that.getBoolean(thatPos); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnFactory.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnFactory.java index ed5b853ff..0f73c9b0e 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnFactory.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnFactory.java @@ -21,32 +21,11 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.enums.TSDataType; -import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; public class ColumnFactory { public static Column create(TSDataType dataType, int initialCapacity) { - switch (dataType) { - case INT64: - case TIMESTAMP: - return new LongColumn(initialCapacity); - case DOUBLE: - return new DoubleColumn(initialCapacity); - case FLOAT: - return new FloatColumn(initialCapacity); - case TEXT: - case STRING: - case BLOB: - case OBJECT: - return new BinaryColumn(initialCapacity); - case INT32: - return new IntColumn(initialCapacity, TSDataType.INT32); - case DATE: - return new IntColumn(initialCapacity, TSDataType.DATE); - case BOOLEAN: - return new BooleanColumn(initialCapacity); - default: - throw new IllegalArgumentException( - Messages.format("error.read.column_factory_unsupported_type", dataType)); - } + Type type = Type.fromTsDataType(dataType); + return type.createColumnWithZeroPosition(initialCapacity); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnUtil.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnUtil.java index 3c741db46..77b5746f6 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnUtil.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/ColumnUtil.java @@ -20,6 +20,7 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; import java.util.Arrays; @@ -37,6 +38,14 @@ public class ColumnUtil { private ColumnUtil() {} + static void checkConversion(TSDataType sourceType, TSDataType targetType) { + requireNonNull(targetType, "type is null"); + if (!targetType.isCompatible(sourceType)) { + throw new ClassCastException( + Messages.format("error.common.unsupported_cast", sourceType, targetType)); + } + } + static void checkArrayRange(int[] array, int offset, int length) { requireNonNull(array, "array is null"); if (offset < 0 || length < 0 || offset + length > array.length) { diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DictionaryColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DictionaryColumn.java index af7f22bef..7ce598483 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DictionaryColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DictionaryColumn.java @@ -27,6 +27,9 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -249,6 +252,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.DICTIONARY; } + @Override + public Column convertTo(TSDataType type) { + TSDataType sourceType = getDataType(); + if (type == sourceType) { + return this; + } + ColumnUtil.checkConversion(sourceType, type); + // Transform dictionary values only, preserving the ids and dictionary encoding. + return new DictionaryColumn( + idsOffset, + positionCount, + dictionary.convertTo(type), + ids, + isCompact(), + isSequentialIds, + randomDictionaryId()); + } + @Override public boolean mayHaveNull() { return mayHaveNull && dictionary.mayHaveNull(); @@ -427,6 +448,21 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return dictionary.getTsPrimitiveType(position); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + dictionary.writeTo(getId(index), buffer); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + dictionary.writeTo(getId(index), stream); + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + return dictionary.arePositionsEqual(getId(thisPos), that, thatPos); + } + @Override public void reset() { throw new UnsupportedOperationException(getClass().getSimpleName()); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DoubleColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DoubleColumn.java index 4ac8d08ae..953f13730 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DoubleColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/DoubleColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; @@ -92,6 +97,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.INT64_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.DOUBLE) { + return this; + } + ColumnUtil.checkConversion(TSDataType.DOUBLE, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else { + builder.writeDouble(getDouble(position)); + } + } + return builder.build(); + } + @Override public double getDouble(int position) { return values[position + arrayOffset]; @@ -126,6 +149,34 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsDouble(getDouble(position)); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.putDouble(values[index + arrayOffset]); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeDouble(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + output.writeLong(Double.doubleToLongBits(values[i + arrayOffset])); + } + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getDouble(thisPos) == that.getDouble(thatPos); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java index d8a31f110..77326c91d 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; @@ -93,6 +98,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.INT32_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.FLOAT) { + return this; + } + ColumnUtil.checkConversion(TSDataType.FLOAT, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else { + builder.writeFloat(getFloat(position)); + } + } + return builder.build(); + } + @Override public float getFloat(int position) { return values[position + arrayOffset]; @@ -141,6 +164,34 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsFloat(getFloat(position)); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.putFloat(values[index + arrayOffset]); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeFloat(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + output.writeInt(Float.floatToIntBits(values[i + arrayOffset])); + } + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getFloat(thisPos) == that.getFloat(thatPos); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int32ArrayColumnEncoder.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int32ArrayColumnEncoder.java index 3c6e9b1af..4e49502cd 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int32ArrayColumnEncoder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int32ArrayColumnEncoder.java @@ -22,6 +22,7 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import java.io.DataOutputStream; import java.io.IOException; @@ -39,42 +40,16 @@ public Column readColumn(ByteBuffer input, TSDataType dataType, int positionCoun // | byte | list[byte] | list[int32] | // +---------------+-----------------+-------------+ - boolean[] nullIndicators = ColumnEncoder.deserializeNullIndicators(input, positionCount); - - switch (dataType) { - case INT32: - case DATE: - int[] intValues = new int[positionCount]; - if (nullIndicators == null) { - for (int i = 0; i < positionCount; i++) { - intValues[i] = input.getInt(); - } - } else { - for (int i = 0; i < positionCount; i++) { - if (!nullIndicators[i]) { - intValues[i] = input.getInt(); - } - } - } - return new IntColumn(0, positionCount, nullIndicators, intValues); - case FLOAT: - float[] floatValues = new float[positionCount]; - if (nullIndicators == null) { - for (int i = 0; i < positionCount; i++) { - floatValues[i] = Float.intBitsToFloat(input.getInt()); - } - } else { - for (int i = 0; i < positionCount; i++) { - if (!nullIndicators[i]) { - floatValues[i] = Float.intBitsToFloat(input.getInt()); - } - } - } - return new FloatColumn(0, positionCount, nullIndicators, floatValues); - default: - throw new IllegalArgumentException( - Messages.format("error.read.col_encoder_invalid_type", dataType)); + if (dataType != TSDataType.INT32 + && dataType != TSDataType.DATE + && dataType != TSDataType.FLOAT) { + throw new IllegalArgumentException( + Messages.format("error.read.col_encoder_invalid_type", dataType)); } + + boolean[] nullIndicators = ColumnEncoder.deserializeNullIndicators(input, positionCount); + return (Column) + Type.fromTsDataType(dataType).deserializeColumn(input, positionCount, nullIndicators); } @Override @@ -83,38 +58,12 @@ public void writeColumn(DataOutputStream output, Column column) throws IOExcepti ColumnEncoder.serializeNullIndicators(output, column); TSDataType dataType = column.getDataType(); - int positionCount = column.getPositionCount(); - switch (dataType) { - case INT32: - case DATE: - if (column.mayHaveNull()) { - for (int i = 0; i < positionCount; i++) { - if (!column.isNull(i)) { - output.writeInt(column.getInt(i)); - } - } - } else { - for (int i = 0; i < positionCount; i++) { - output.writeInt(column.getInt(i)); - } - } - break; - case FLOAT: - if (column.mayHaveNull()) { - for (int i = 0; i < positionCount; i++) { - if (!column.isNull(i)) { - output.writeInt(Float.floatToIntBits(column.getFloat(i))); - } - } - } else { - for (int i = 0; i < positionCount; i++) { - output.writeInt(Float.floatToIntBits(column.getFloat(i))); - } - } - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.col_encoder_invalid_type", dataType)); + if (dataType != TSDataType.INT32 + && dataType != TSDataType.DATE + && dataType != TSDataType.FLOAT) { + throw new IllegalArgumentException( + Messages.format("error.read.col_encoder_invalid_type", dataType)); } + column.serializeWithoutNulls(output); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int64ArrayColumnEncoder.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int64ArrayColumnEncoder.java index 0076c88f0..e7e3272c1 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int64ArrayColumnEncoder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/Int64ArrayColumnEncoder.java @@ -22,6 +22,7 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import java.io.DataOutputStream; import java.io.IOException; @@ -39,41 +40,16 @@ public Column readColumn(ByteBuffer input, TSDataType dataType, int positionCoun // | byte | list[byte] | list[int64] | // +---------------+-----------------+-------------+ - boolean[] nullIndicators = ColumnEncoder.deserializeNullIndicators(input, positionCount); - switch (dataType) { - case INT64: - case TIMESTAMP: - long[] values = new long[positionCount]; - if (nullIndicators == null) { - for (int i = 0; i < positionCount; i++) { - values[i] = input.getLong(); - } - } else { - for (int i = 0; i < positionCount; i++) { - if (!nullIndicators[i]) { - values[i] = input.getLong(); - } - } - } - return new LongColumn(0, positionCount, nullIndicators, values); - case DOUBLE: - double[] doubleValues = new double[positionCount]; - if (nullIndicators == null) { - for (int i = 0; i < positionCount; i++) { - doubleValues[i] = Double.longBitsToDouble(input.getLong()); - } - } else { - for (int i = 0; i < positionCount; i++) { - if (!nullIndicators[i]) { - doubleValues[i] = Double.longBitsToDouble(input.getLong()); - } - } - } - return new DoubleColumn(0, positionCount, nullIndicators, doubleValues); - default: - throw new IllegalArgumentException( - Messages.format("error.read.col_encoder_invalid_type", dataType)); + if (dataType != TSDataType.INT64 + && dataType != TSDataType.TIMESTAMP + && dataType != TSDataType.DOUBLE) { + throw new IllegalArgumentException( + Messages.format("error.read.col_encoder_invalid_type", dataType)); } + + boolean[] nullIndicators = ColumnEncoder.deserializeNullIndicators(input, positionCount); + return (Column) + Type.fromTsDataType(dataType).deserializeColumn(input, positionCount, nullIndicators); } @Override @@ -82,26 +58,12 @@ public void writeColumn(DataOutputStream output, Column column) throws IOExcepti ColumnEncoder.serializeNullIndicators(output, column); TSDataType dataType = column.getDataType(); - int positionCount = column.getPositionCount(); - switch (dataType) { - case INT64: - case TIMESTAMP: - for (int i = 0; i < positionCount; i++) { - if (!column.isNull(i)) { - output.writeLong(column.getLong(i)); - } - } - break; - case DOUBLE: - for (int i = 0; i < positionCount; i++) { - if (!column.isNull(i)) { - output.writeLong(Double.doubleToLongBits(column.getDouble(i))); - } - } - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.col_encoder_invalid_type", dataType)); + if (dataType != TSDataType.INT64 + && dataType != TSDataType.TIMESTAMP + && dataType != TSDataType.DOUBLE) { + throw new IllegalArgumentException( + Messages.format("error.read.col_encoder_invalid_type", dataType)); } + column.serializeWithoutNulls(output); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java index fbea95799..810450605 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; @@ -72,7 +77,7 @@ public IntColumn( this.dataType = dataType; } - IntColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, int[] values) { + public IntColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, int[] values) { if (arrayOffset < 0) { throw new IllegalArgumentException(Messages.get("error.read.col_array_offset_negative")); } @@ -111,8 +116,9 @@ public TSDataType getDataType() { return dataType; } - public void modifyDataType(TSDataType dataType) { + public IntColumn modifyDataType(TSDataType dataType) { this.dataType = dataType; + return this; } @Override @@ -120,6 +126,28 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.INT32_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == dataType) { + return this; + } + ColumnUtil.checkConversion(dataType, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else if (dataType == TSDataType.DATE) { + // DATE has an int physical representation but a formatted text cast. + builder.writeBinary( + new Binary(TSDataType.getDateStringValue(getInt(position)), StandardCharsets.UTF_8)); + } else { + builder.writeInt(getInt(position)); + } + } + return builder.build(); + } + @Override public int getInt(int position) { return values[position + arrayOffset]; @@ -196,6 +224,34 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsInt(getInt(position), dataType); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.putInt(values[index + arrayOffset]); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeInt(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + output.writeInt(values[i + arrayOffset]); + } + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getInt(thisPos) == that.getInt(thatPos); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java index 5bf01c752..7abedf523 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java @@ -20,13 +20,18 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; @@ -59,7 +64,7 @@ public LongColumn(int positionCount, Optional valueIsNull, long[] val this(0, positionCount, valueIsNull.orElse(null), values); } - LongColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, long[] values) { + public LongColumn(int arrayOffset, int positionCount, boolean[] valueIsNull, long[] values) { if (arrayOffset < 0) { throw new IllegalArgumentException(Messages.get("error.read.col_array_offset_negative")); } @@ -93,6 +98,24 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.INT64_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.INT64) { + return this; + } + ColumnUtil.checkConversion(TSDataType.INT64, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + if (isNull(position)) { + builder.appendNull(); + } else { + builder.writeLong(getLong(position)); + } + } + return builder.build(); + } + @Override public long getLong(int position) { return values[position + arrayOffset]; @@ -141,6 +164,34 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return new TsPrimitiveType.TsLong(getLong(position)); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.putLong(values[index + arrayOffset]); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeLong(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + if (!isNull(i)) { + output.writeLong(values[i + arrayOffset]); + } + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + boolean thisIsNull = isNull(thisPos); + if (thisIsNull) { + return that.isNull(thatPos); + } + return !that.isNull(thatPos) && getLong(thisPos) == that.getLong(thatPos); + } + @Override public boolean mayHaveNull() { return valueIsNull != null; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/NullColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/NullColumn.java index 75c32ad61..75fea0ed5 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/NullColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/NullColumn.java @@ -23,8 +23,12 @@ import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.RamUsageEstimator; +import java.io.DataOutputStream; +import java.nio.ByteBuffer; + import static java.util.Objects.requireNonNull; import static org.apache.tsfile.read.common.block.column.ColumnUtil.checkArrayRange; import static org.apache.tsfile.read.common.block.column.ColumnUtil.checkReadablePosition; @@ -61,6 +65,11 @@ public ColumnEncoding getEncoding() { throw new UnsupportedOperationException(getClass().getName()); } + @Override + public Column convertTo(TSDataType type) { + return create(type, positionCount); + } + @Override public boolean mayHaveNull() { return true; @@ -71,11 +80,31 @@ public boolean isNull(int position) { return true; } + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + return that.isNull(thatPos); + } + @Override public boolean[] isNull() { throw new UnsupportedOperationException(getClass().getName()); } + @Override + public void serializeWithoutNulls(DataOutputStream output) { + // There are no non-null values to serialize. + } + + @Override + public void writeTo(int index, ByteBuffer buffer) { + // There is no value to serialize. + } + + @Override + public void writeTo(int index, DataOutputStream stream) { + // There is no value to serialize. + } + @Override public int getPositionCount() { return positionCount; @@ -139,28 +168,7 @@ public void reverse() { public static Column create(TSDataType dataType, int positionCount) { requireNonNull(dataType, "dataType is null"); - switch (dataType) { - case BOOLEAN: - return new RunLengthEncodedColumn(BooleanColumnBuilder.NULL_VALUE_BLOCK, positionCount); - case INT32: - case DATE: - return new RunLengthEncodedColumn(IntColumnBuilder.NULL_VALUE_BLOCK, positionCount); - case INT64: - case TIMESTAMP: - return new RunLengthEncodedColumn(LongColumnBuilder.NULL_VALUE_BLOCK, positionCount); - case FLOAT: - return new RunLengthEncodedColumn(FloatColumnBuilder.NULL_VALUE_BLOCK, positionCount); - case DOUBLE: - return new RunLengthEncodedColumn(DoubleColumnBuilder.NULL_VALUE_BLOCK, positionCount); - case TEXT: - case BLOB: - case STRING: - case OBJECT: - return new RunLengthEncodedColumn(BinaryColumnBuilder.NULL_VALUE_BLOCK, positionCount); - default: - throw new IllegalArgumentException( - Messages.format("error.read.null_col_unknown_type", dataType)); - } + return Type.fromTsDataType(dataType).createNullColumn(positionCount); } @Override diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/RunLengthEncodedColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/RunLengthEncodedColumn.java index 4414e24de..8c39c9549 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/RunLengthEncodedColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/RunLengthEncodedColumn.java @@ -27,6 +27,9 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Arrays; import static java.util.Objects.requireNonNull; @@ -76,6 +79,17 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.RLE; } + @Override + public Column convertTo(TSDataType type) { + TSDataType sourceType = getDataType(); + if (type == sourceType) { + return this; + } + ColumnUtil.checkConversion(sourceType, type); + // Transform the single stored value and keep the run-length representation. + return new RunLengthEncodedColumn(value.convertTo(type), positionCount); + } + @Override public boolean getBoolean(int position) { return value.getBoolean(0); @@ -165,6 +179,21 @@ public TsPrimitiveType getTsPrimitiveType(int position) { return value.getTsPrimitiveType(0); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + value.writeTo(0, buffer); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + value.writeTo(0, stream); + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + return value.arePositionsEqual(0, that, thatPos); + } + @Override public boolean mayHaveNull() { return value.mayHaveNull(); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/TimeColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/TimeColumn.java index 45053b971..d0dc19377 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/TimeColumn.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/TimeColumn.java @@ -20,11 +20,16 @@ package org.apache.tsfile.read.common.block.column; import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.block.column.ColumnEncoding; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.RamUsageEstimator; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Arrays; import static org.apache.tsfile.read.common.block.column.ColumnUtil.checkArrayRange; @@ -80,6 +85,20 @@ public ColumnEncoding getEncoding() { return ColumnEncoding.INT64_ARRAY; } + @Override + public Column convertTo(TSDataType type) { + if (type == TSDataType.INT64) { + return this; + } + ColumnUtil.checkConversion(TSDataType.INT64, type); + + ColumnBuilder builder = Type.fromTsDataType(type).createColumnBuilder(positionCount); + for (int position = 0; position < positionCount; position++) { + builder.writeLong(getLong(position)); + } + return builder.build(); + } + @Override public long getLong(int position) { return values[position + arrayOffset]; @@ -90,6 +109,28 @@ public Object getObject(int position) { return getLong(position); } + @Override + public void writeTo(int index, ByteBuffer buffer) { + buffer.putLong(values[index + arrayOffset]); + } + + @Override + public void writeTo(int index, DataOutputStream stream) throws IOException { + stream.writeLong(values[index + arrayOffset]); + } + + @Override + public void serializeWithoutNulls(DataOutputStream output) throws IOException { + for (int i = 0; i < positionCount; i++) { + output.writeLong(values[i + arrayOffset]); + } + } + + @Override + public boolean arePositionsEqual(int thisPos, Column that, int thatPos) { + return !that.isNull(thatPos) && getLong(thisPos) == that.getLong(thatPos); + } + public boolean mayHaveNull() { return false; } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractIntType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractIntType.java index 8425a8793..3d29a5b50 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractIntType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractIntType.java @@ -21,13 +21,299 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.DeltaBinaryDecoder; +import org.apache.tsfile.encoding.decoder.IntChimpDecoder; +import org.apache.tsfile.encoding.decoder.IntGorillaDecoder; +import org.apache.tsfile.encoding.decoder.IntRLBEDecoder; +import org.apache.tsfile.encoding.decoder.IntRleDecoder; +import org.apache.tsfile.encoding.decoder.IntSprintzDecoder; +import org.apache.tsfile.encoding.decoder.IntZigzagDecoder; +import org.apache.tsfile.encoding.decoder.RegularDataDecoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.IntegerStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.IntColumn; import org.apache.tsfile.read.common.block.column.IntColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.IntDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public abstract class AbstractIntType extends AbstractType { + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + BytesUtils.intToBytes(value.getInt(), valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return Integer.BYTES; + } + + @Override + public int calcTypeSize(Object value) { + return Integer.BYTES; + } + + @Override + public void init(BatchData batchData) { + batchData.initIntValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putInt(timestamp, (int) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + outputStream.writeInt(batchData.getIntByIndex(index)); + } + return Math.multiplyExact(Long.BYTES + Integer.BYTES, batchData.length()); + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + stream.writeInt(value.getInt()); + return Integer.BYTES; + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + buffer.putInt((int) value); + return Integer.BYTES; + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + stream.writeInt((int) value); + return Integer.BYTES; + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getInt32Encoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getInt32Compressor(); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE -> new IntRleDecoder(); + case TS_2DIFF -> new DeltaBinaryDecoder.IntDeltaDecoder(); + case REGULAR -> new RegularDataDecoder.IntRegularDecoder(); + case GORILLA -> new IntGorillaDecoder(); + case ZIGZAG -> new IntZigzagDecoder(); + case CHIMP -> new IntChimpDecoder(); + case SPRINTZ -> new IntSprintzDecoder(); + case RLBE -> new IntRLBEDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Integer.BYTES; + } + + @Override + public Statistics createStatistics() { + return new IntegerStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return IntegerStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getInt()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getInt()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getInt(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getInt(); + } + + @Override + public String toString(Field field) { + return String.valueOf(field.getIntV()); + } + + @Override + public Object getValue(Field field) { + return field.getIntV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setIntV(from.getInt()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getInts()[index] = from.getInt(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getInts(), startIndex, endIndex, (int) value); + } + + @Override + public void setTo(Field from, Field to) { + to.setIntV(from.getIntV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setIntV(from.getInt()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getInts()[index] = from.getInt(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getInts()[toIndex] = from.getInt(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((int[]) toArray)[toIndex] = from.getInt(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getIntV()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getInt(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new IntDataPoint(measurementId, Integer.parseInt(value)); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new IntDataPoint(measurementId, (int) value); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? 0 : (int) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getInt() : Integer.MAX_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((int[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? 0 : column.getInt(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getInts(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((int[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getInt()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getInt()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (int) value); + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsInt(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsInt((int) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsInt(((int[]) array)[rowIndex]); + } + @Override public int getInt(Column c, int position) { return c.getInt(position); @@ -48,6 +334,31 @@ public double getDouble(Column c, int position) { return c.getInt(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeInt(value.getInt()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + builder.writeInt(BytesUtils.bytesToInt(bytes, offset)); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeInt(column.getInt(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((int[]) toArray)[index] = from.getInt(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((int[]) toArray)[index] = from.getInt(); + } + @Override public void writeInt(ColumnBuilder builder, int value) { builder.writeInt(value); @@ -68,11 +379,145 @@ public void writeDouble(ColumnBuilder builder, double value) { builder.writeInt((int) value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + checkValueType(value, Integer.class, "Integer"); + ((int[]) array)[rowIndex] = value != null ? (int) value : Integer.MIN_VALUE; + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((int[]) target)[targetIndex] = ((int[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((int[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new int[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfIntArray(size); + } + + @Override + public long estimateValueSize() { + return Integer.BYTES; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return Math.multiplyExact(Integer.BYTES, rowSize); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + int[] values = (int[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(values[i], stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + int[] values = (int[]) array; + for (int i = 0; i < length; i++) { + buffer.putInt(values[i]); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + int[] values = new int[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readInt(buffer); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + int[] values = new int[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readInt(stream); + } + return values; + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readInt(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readInt(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + int[] intValues = new int[rowSize]; + if (nullIndicators == null) { + for (int i = 0; i < rowSize; i++) { + intValues[i] = buffer.getInt(); + } + } else { + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + intValues[i] = buffer.getInt(); + } + } + } + return new IntColumn(0, rowSize, nullIndicators, intValues); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return hasEnoughLength(left, right, rowSize) + && Arrays.equals((int[]) left, 0, rowSize, (int[]) right, 0, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((int[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new IntColumn( + positionCount, + Optional.of(new boolean[positionCount]), + new int[positionCount], + TSDataType.INT32); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new IntColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(IntColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new IntColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new IntColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public boolean isComparable() { return true; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractLongType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractLongType.java index e8e1ecbc3..6555b3962 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractLongType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractLongType.java @@ -21,13 +21,298 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.DeltaBinaryDecoder; +import org.apache.tsfile.encoding.decoder.LongChimpDecoder; +import org.apache.tsfile.encoding.decoder.LongGorillaDecoder; +import org.apache.tsfile.encoding.decoder.LongRLBEDecoder; +import org.apache.tsfile.encoding.decoder.LongRleDecoder; +import org.apache.tsfile.encoding.decoder.LongSprintzDecoder; +import org.apache.tsfile.encoding.decoder.LongZigzagDecoder; +import org.apache.tsfile.encoding.decoder.RegularDataDecoder; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.LongStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.LongColumn; import org.apache.tsfile.read.common.block.column.LongColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.LongDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public abstract class AbstractLongType extends AbstractType { + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + BytesUtils.longToBytes(value.getLong(), valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return Long.BYTES; + } + + @Override + public int calcTypeSize(Object value) { + return Long.BYTES; + } + + @Override + public void init(BatchData batchData) { + batchData.initLongValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putLong(timestamp, (long) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + outputStream.writeLong(batchData.getLongByIndex(index)); + } + return Math.multiplyExact(Long.BYTES + Long.BYTES, batchData.length()); + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + stream.writeLong(value.getLong()); + return Long.BYTES; + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + buffer.putLong((long) value); + return Long.BYTES; + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + stream.writeLong((long) value); + return Long.BYTES; + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getInt64Encoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getInt64Compressor(); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE -> new LongRleDecoder(); + case TS_2DIFF -> new DeltaBinaryDecoder.LongDeltaDecoder(); + case REGULAR -> new RegularDataDecoder.LongRegularDecoder(); + case GORILLA -> new LongGorillaDecoder(); + case ZIGZAG -> new LongZigzagDecoder(); + case CHIMP -> new LongChimpDecoder(); + case SPRINTZ -> new LongSprintzDecoder(); + case RLBE -> new LongRLBEDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Long.BYTES; + } + + @Override + public Statistics createStatistics() { + return new LongStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return LongStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getLong()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getLong()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getLong(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getLong(); + } + + @Override + public String toString(Field field) { + return String.valueOf(field.getLongV()); + } + + @Override + public Object getValue(Field field) { + return field.getLongV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setLongV(from.getLong()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getLongs()[index] = from.getLong(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getLongs(), startIndex, endIndex, (long) value); + } + + @Override + public void setTo(Field from, Field to) { + to.setLongV(from.getLongV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setLongV(from.getLong()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getLongs()[index] = from.getLong(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getLongs()[toIndex] = from.getLong(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((long[]) toArray)[toIndex] = from.getLong(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getLongV()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getLong(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new LongDataPoint(measurementId, Long.parseLong(value)); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new LongDataPoint(measurementId, value); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? 0L : (long) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getLong() : Long.MAX_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((long[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? 0 : column.getLong(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getLongs(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((long[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getLong()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getLong()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (long) value); + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsLong(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsLong((long) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsLong(((long[]) array)[rowIndex]); + } + @Override public int getInt(Column c, int position) { return (int) c.getLong(position); @@ -48,6 +333,31 @@ public double getDouble(Column c, int position) { return c.getLong(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeLong(value.getLong()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + builder.writeLong(BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset)); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeLong(column.getLong(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((long[]) toArray)[index] = from.getLong(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((long[]) toArray)[index] = from.getLong(); + } + @Override public void writeInt(ColumnBuilder builder, int value) { builder.writeLong(value); @@ -68,11 +378,142 @@ public void writeDouble(ColumnBuilder builder, double value) { builder.writeLong((long) value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + checkValueType(value, Long.class, "Long"); + ((long[]) array)[rowIndex] = value != null ? (long) value : Long.MIN_VALUE; + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((long[]) target)[targetIndex] = ((long[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((long[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new long[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfLongArray(size); + } + + @Override + public long estimateValueSize() { + return Long.BYTES; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return Math.multiplyExact(Long.BYTES, rowSize); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + long[] values = (long[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(values[i], stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + long[] values = (long[]) array; + for (int i = 0; i < length; i++) { + buffer.putLong(values[i]); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + long[] values = new long[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readLong(buffer); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + long[] values = new long[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readLong(stream); + } + return values; + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readLong(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readLong(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + long[] intValues = new long[rowSize]; + if (nullIndicators == null) { + for (int i = 0; i < rowSize; i++) { + intValues[i] = buffer.getLong(); + } + } else { + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + intValues[i] = buffer.getLong(); + } + } + } + return new LongColumn(0, rowSize, nullIndicators, intValues); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return hasEnoughLength(left, right, rowSize) + && Arrays.equals((long[]) left, 0, rowSize, (long[]) right, 0, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((long[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new LongColumn( + positionCount, Optional.of(new boolean[positionCount]), new long[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new LongColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(LongColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new LongColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new LongColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public boolean isComparable() { return true; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractType.java index 937b7fdcc..2afe06758 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractType.java @@ -19,8 +19,162 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.DictionaryDecoder; +import org.apache.tsfile.encoding.decoder.PlainDecoder; +import org.apache.tsfile.exception.encoding.TsFileDecodingException; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.Objects; + public abstract class AbstractType implements Type { + private static final String DECODER_NOT_FOUND_ERROR_KEY = "error.encoding.decoder_not_found"; + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN -> new PlainDecoder(); + case DICTIONARY -> new DictionaryDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + protected TsFileDecodingException decoderNotFound(TSEncoding encoding) { + return new TsFileDecodingException( + Messages.format(DECODER_NOT_FOUND_ERROR_KEY, encoding, getTypeEnum())); + } + + protected void binaryToBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + Binary binary = value.getBinary(); + BytesUtils.intToBytes(binary.getLength(), valueBytes, offset); + System.arraycopy(binary.getValues(), 0, valueBytes, offset + Integer.BYTES, binary.getLength()); + } + + protected int binaryTypeSize(TsPrimitiveType value) { + return Integer.BYTES + value.getBinary().getLength(); + } + + protected int binaryTypeSize(Object value) { + return Integer.BYTES + ((Binary) value).getLength(); + } + + protected void checkValueType(Object value, Class expectedClass, String expectedType) { + if (value != null && !expectedClass.isInstance(value)) { + throw new IllegalArgumentException( + Messages.format( + "error.write.tablet_expected_type", + expectedType, + getDisplayName(), + value.getClass().getName())); + } + } + + protected int serializedSizeOfBinaryValues(Object array, int rowSize) { + return serializedSizeOfBinaryValues(array, 0, rowSize); + } + + protected int serializedSizeOfBinaryValues(Object array, int startRow, int endRow) { + Binary[] binaryValues = (Binary[]) array; + int size = 0; + for (int i = startRow; i < endRow; i++) { + size = Math.addExact(size, Byte.BYTES); + if (binaryValues[i] != null) { + size = Math.addExact(size, ReadWriteIOUtils.sizeToWrite(binaryValues[i])); + } + } + return size; + } + + protected void serializeBinaryValues(Object array, int rowSize, DataOutputStream stream) + throws IOException { + Binary[] binaryValues = (Binary[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(BytesUtils.boolToByte(binaryValues[i] != null), stream); + if (binaryValues[i] != null) { + ReadWriteIOUtils.write(binaryValues[i], stream); + } + } + } + + protected void serializeBinaryValues(Object array, int length, ByteBuffer buffer) { + Binary[] binaryValues = (Binary[]) array; + for (int i = 0; i < length; i++) { + ReadWriteIOUtils.write(BytesUtils.boolToByte(binaryValues[i] != null), buffer); + if (binaryValues[i] != null) { + ReadWriteIOUtils.write(binaryValues[i], buffer); + } + } + } + + protected Binary[] deserializeBinaryValues(ByteBuffer buffer, int rowSize) { + Binary[] values = new Binary[rowSize]; + for (int i = 0; i < rowSize; i++) { + boolean isNotNull = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(buffer)); + values[i] = isNotNull ? ReadWriteIOUtils.readBinary(buffer) : Binary.EMPTY_VALUE; + } + return values; + } + + protected Binary[] deserializeBinaryValues(DataInputStream stream, int rowSize) + throws IOException { + Binary[] values = new Binary[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = stream.readBoolean() ? ReadWriteIOUtils.readBinary(stream) : Binary.EMPTY_VALUE; + } + return values; + } + + protected Binary[] deserializeBinaryValues( + ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + Binary[] values = new Binary[rowSize]; + if (nullIndicators == null) { + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readBinary(buffer); + } + } else { + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + values[i] = ReadWriteIOUtils.readBinary(buffer); + } + } + } + return values; + } + + protected boolean hasEnoughLength(Object left, Object right, int rowSize) { + return Array.getLength(left) >= rowSize && Array.getLength(right) >= rowSize; + } + + protected boolean binaryArrayEquals(Object left, Object right, int rowSize) { + if (!hasEnoughLength(left, right, rowSize)) { + return false; + } + Binary[] leftValues = (Binary[]) left; + Binary[] rightValues = (Binary[]) right; + for (int i = 0; i < rowSize; i++) { + if (!Objects.equals(leftValues[i], rightValues[i])) { + return false; + } + } + return true; + } + + @Override + public Object getValue(Object array, int rowIndex) { + return Array.get(array, rowIndex); + } + @Override public String toString() { return getDisplayName(); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractVarcharType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractVarcharType.java index d09ed07c6..520f16444 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractVarcharType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/AbstractVarcharType.java @@ -21,28 +21,445 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.BinaryStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.BinaryColumn; import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.StringDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public abstract class AbstractVarcharType extends AbstractType { + + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + binaryToBytes(value, valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return binaryTypeSize(value); + } + + @Override + public int calcTypeSize(Object value) { + return binaryTypeSize(value); + } + + @Override + public void init(BatchData batchData) { + batchData.initBinaryValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putBinary(timestamp, (Binary) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + int size = 0; + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + Binary binary = batchData.getBinaryByIndex(index); + outputStream.writeInt(binary.getLength()); + outputStream.write(binary.getValues()); + size = Math.addExact(size, Long.BYTES + Integer.BYTES); + size = Math.addExact(size, binary.getLength()); + } + return size; + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + Binary binary = value.getBinary(); + stream.writeInt(binary.getLength()); + stream.write(binary.getValues()); + return Math.addExact(Integer.BYTES, binary.getLength()); + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + return ReadWriteIOUtils.write((Binary) value, buffer); + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + return ReadWriteIOUtils.write((Binary) value, stream); + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getTextEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getTextCompressor(); + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * valveLength; + } + + @Override + public Statistics createStatistics() { + return new BinaryStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return BinaryStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getBinary()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getBinary()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getBinary(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getBinary(); + } + + @Override + public String toString(Field field) { + return field.getBinaryV().toString(); + } + + @Override + public Object getValue(Field field) { + return field.getBinaryV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill( + to.getBinaries(), startIndex, endIndex, value == null ? null : toBinaryValue(value)); + } + + @Override + public void setTo(Field from, Field to) { + to.setBinaryV(from.getBinaryV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getBinaries()[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((Binary[]) toArray)[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getStringValue()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getString(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new StringDataPoint(measurementId, new Binary(value, TSFileConfig.STRING_CHARSET)); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? Binary.EMPTY_VALUE : (Binary) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getBinary() : Binary.EMPTY_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((Binary[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? null : column.getBinary(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getBinaries(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((Binary[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (Binary) value); + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsBinary(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsBinary((Binary) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsBinary(((Binary[]) array)[rowIndex]); + } + @Override public Binary getBinary(Column c, int position) { return c.getBinary(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeBinary(value.getBinary()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + int length = BytesUtils.bytesToInt(bytes, offset); + builder.writeBinary( + new Binary( + Arrays.copyOfRange(bytes, offset + Integer.BYTES, offset + Integer.BYTES + length))); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeBinary(column.getBinary(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + @Override public void writeBinary(ColumnBuilder builder, Binary value) { builder.writeBinary(value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + ((Binary[]) array)[rowIndex] = toBinaryValue(value); + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((Binary[]) target)[targetIndex] = ((Binary[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((Binary[]) array, newLength); + } + + protected Binary toBinaryValue(Object value) { + if (value != null && !(value instanceof Binary) && !(value instanceof String)) { + throw new IllegalArgumentException( + Messages.format( + "error.write.tablet_expected_type", + "Binary or String", + getDisplayName(), + value.getClass().getName())); + } + if (value instanceof Binary) { + return (Binary) value; + } + return value != null + ? new Binary(((String) value).getBytes(TSFileConfig.STRING_CHARSET)) + : Binary.EMPTY_VALUE; + } + + @Override + public Object createArray(int capacity) { + return new Binary[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfObjectArray(size); + } + + @Override + public long estimateValueSize() { + return RamUsageEstimator.NUM_BYTES_OBJECT_REF; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return serializedSizeOfBinaryValues(array, rowSize); + } + + @Override + public int serializedSize(Object array, int startRow, int endRow) { + return serializedSizeOfBinaryValues(array, startRow, endRow); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + serializeBinaryValues(array, rowSize, stream); + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + serializeBinaryValues(array, length, buffer); + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + return deserializeBinaryValues(buffer, rowSize); + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + return deserializeBinaryValues(stream, rowSize); + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readBinary(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readBinary(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + Binary[] values = deserializeBinaryValues(buffer, rowSize, nullIndicators); + return new BinaryColumn(0, rowSize, nullIndicators, values); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return binaryArrayEquals(left, right, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((Binary[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new BinaryColumn( + positionCount, Optional.of(new boolean[positionCount]), new Binary[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new BinaryColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(BinaryColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new BinaryColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new BinaryColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public boolean isComparable() { return true; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BinaryType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BinaryType.java index 68c874998..ece9ba52b 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BinaryType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BinaryType.java @@ -19,11 +19,22 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.utils.Binary; + public class BinaryType extends AbstractVarcharType { public static final BinaryType TEXT = new BinaryType(); private BinaryType() {} + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.TEXT); + field.setBinaryV((Binary) value); + return field; + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.TEXT; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BlobType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BlobType.java index d02ca7168..c0c1e8b81 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BlobType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BlobType.java @@ -21,11 +21,41 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.BlobStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.BinaryColumn; import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.StringDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public class BlobType extends AbstractType { @@ -33,21 +63,418 @@ public class BlobType extends AbstractType { private BlobType() {} + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + binaryToBytes(value, valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return binaryTypeSize(value); + } + + @Override + public int calcTypeSize(Object value) { + return binaryTypeSize(value); + } + + @Override + public void init(BatchData batchData) { + batchData.initBinaryValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putBinary(timestamp, (Binary) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + int size = 0; + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + Binary binary = batchData.getBinaryByIndex(index); + outputStream.writeInt(binary.getLength()); + outputStream.write(binary.getValues()); + size = Math.addExact(size, Long.BYTES + Integer.BYTES); + size = Math.addExact(size, binary.getLength()); + } + return size; + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + Binary binary = value.getBinary(); + stream.writeInt(binary.getLength()); + stream.write(binary.getValues()); + return Math.addExact(Integer.BYTES, binary.getLength()); + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + return ReadWriteIOUtils.write((Binary) value, buffer); + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + return ReadWriteIOUtils.write((Binary) value, stream); + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getTextEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getTextCompressor(); + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * valveLength; + } + + @Override + public Statistics createStatistics() { + return new BlobStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return BlobStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getBinary()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getBinary()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getBinary(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getBinary(); + } + + @Override + public String toString(Field field) { + return BytesUtils.parseBlobByteArrayToString(field.getBinaryV().getValues()); + } + + @Override + public Object getValue(Field field) { + return field.getBinaryV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + if (value == null) { + Arrays.fill(to.getBinaries(), startIndex, endIndex, null); + return; + } + Binary[] values = new Binary[1]; + addValue(0, value, values); + Arrays.fill(to.getBinaries(), startIndex, endIndex, values[0]); + } + + @Override + public void setTo(Field from, Field to) { + to.setBinaryV(from.getBinaryV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getBinaries()[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((Binary[]) toArray)[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getBinaryV().getValues()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getBinary(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new StringDataPoint(measurementId, new Binary(value, TSFileConfig.STRING_CHARSET)); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? Binary.EMPTY_VALUE : (Binary) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getBinary() : Binary.EMPTY_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((Binary[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? null : column.getBinary(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getBinaries(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((Binary[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (Binary) value); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.BLOB); + field.setBinaryV((Binary) value); + return field; + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsBinary(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsBinary((Binary) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsBinary(((Binary[]) array)[rowIndex]); + } + @Override public Binary getBinary(Column c, int position) { return c.getBinary(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeBinary(value.getBinary()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + int length = BytesUtils.bytesToInt(bytes, offset); + builder.writeBinary( + new Binary( + Arrays.copyOfRange(bytes, offset + Integer.BYTES, offset + Integer.BYTES + length))); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeBinary(column.getBinary(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + @Override public void writeBinary(ColumnBuilder builder, Binary value) { builder.writeBinary(value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + if (value != null && !(value instanceof Binary) && !(value instanceof String)) { + throw new IllegalArgumentException( + Messages.format( + "error.write.tablet_expected_type", + "Binary or String", + getDisplayName(), + value.getClass().getName())); + } + if (value instanceof Binary) { + ((Binary[]) array)[rowIndex] = (Binary) value; + } else { + ((Binary[]) array)[rowIndex] = + value != null + ? new Binary(((String) value).getBytes(TSFileConfig.STRING_CHARSET)) + : Binary.EMPTY_VALUE; + } + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((Binary[]) target)[targetIndex] = ((Binary[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((Binary[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new Binary[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfObjectArray(size); + } + + @Override + public long estimateValueSize() { + return RamUsageEstimator.NUM_BYTES_OBJECT_REF; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return serializedSizeOfBinaryValues(array, rowSize); + } + + @Override + public int serializedSize(Object array, int startRow, int endRow) { + return serializedSizeOfBinaryValues(array, startRow, endRow); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + serializeBinaryValues(array, rowSize, stream); + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + serializeBinaryValues(array, length, buffer); + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + return deserializeBinaryValues(buffer, rowSize); + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + return deserializeBinaryValues(stream, rowSize); + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readBinary(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readBinary(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + Binary[] binaries = deserializeBinaryValues(buffer, rowSize, nullIndicators); + return new BinaryColumn(0, rowSize, nullIndicators, binaries); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return binaryArrayEquals(left, right, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((Binary[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new BinaryColumn( + positionCount, Optional.of(new boolean[positionCount]), new Binary[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new BinaryColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(BinaryColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new BinaryColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new BinaryColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.BLOB; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BooleanType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BooleanType.java index e00d7ecb8..55370db6e 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BooleanType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/BooleanType.java @@ -21,10 +21,42 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.IntRleDecoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.BooleanStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.BooleanColumn; import org.apache.tsfile.read.common.block.column.BooleanColumnBuilder; +import org.apache.tsfile.read.common.block.column.ColumnEncoder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.BooleanDataPoint; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public class BooleanType extends AbstractType { @@ -32,21 +64,433 @@ public class BooleanType extends AbstractType { private BooleanType() {} + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + BytesUtils.boolToBytes(value.getBoolean(), valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return Byte.BYTES; + } + + @Override + public int calcTypeSize(Object value) { + return Byte.BYTES; + } + + @Override + public void init(BatchData batchData) { + batchData.initBooleanValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putBoolean(timestamp, (boolean) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + outputStream.writeBoolean(batchData.getBooleanByIndex(index)); + } + return Math.multiplyExact(Long.BYTES + Byte.BYTES, batchData.length()); + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + stream.writeBoolean(value.getBoolean()); + return Byte.BYTES; + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + buffer.put((boolean) value ? (byte) 1 : (byte) 0); + return Byte.BYTES; + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + stream.writeBoolean((boolean) value); + return Byte.BYTES; + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getBooleanEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getBooleanCompressor(); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE -> new IntRleDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Byte.BYTES; + } + + @Override + public Statistics createStatistics() { + return new BooleanStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return BooleanStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getBoolean()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getBoolean()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getBoolean(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getBoolean(); + } + + @Override + public String toString(Field field) { + return String.valueOf(field.getBoolV()); + } + + @Override + public Object getValue(Field field) { + return field.getBoolV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setBoolV(from.getBoolean()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getBooleans()[index] = from.getBoolean(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getBooleans(), startIndex, endIndex, (boolean) value); + } + + @Override + public void setTo(Field from, Field to) { + to.setBoolV(from.getBoolV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setBoolV(from.getBoolean()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getBooleans()[index] = from.getBoolean(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getBooleans()[toIndex] = from.getBoolean(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((boolean[]) toArray)[toIndex] = from.getBoolean(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getBoolV()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getBoolean(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new BooleanDataPoint(measurementId, Boolean.parseBoolean(value)); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new BooleanDataPoint(measurementId, true); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, !isNull && (boolean) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null && value.getBoolean(), value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((boolean[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? false : column.getBoolean(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getBooleans(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((boolean[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getBoolean()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getBoolean()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (boolean) value); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.BOOLEAN); + field.setBoolV((boolean) value); + return field; + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsBoolean(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsBoolean((boolean) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsBoolean(((boolean[]) array)[rowIndex]); + } + @Override public boolean getBoolean(Column c, int position) { return c.getBoolean(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeBoolean(value.getBoolean()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + builder.writeBoolean(BytesUtils.bytesToBool(bytes, offset)); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeBoolean(column.getBoolean(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((boolean[]) toArray)[index] = from.getBoolean(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((boolean[]) toArray)[index] = from.getBoolean(); + } + @Override public void writeBoolean(ColumnBuilder builder, boolean value) { builder.writeBoolean(value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + checkValueType(value, Boolean.class, "Boolean"); + ((boolean[]) array)[rowIndex] = value != null && (boolean) value; + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((boolean[]) target)[targetIndex] = ((boolean[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((boolean[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new boolean[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfBooleanArray(size); + } + + @Override + public long estimateValueSize() { + return Byte.BYTES; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return rowSize; + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + boolean[] values = (boolean[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(BytesUtils.boolToByte(values[i]), stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + boolean[] values = (boolean[]) array; + for (int i = 0; i < length; i++) { + buffer.put(BytesUtils.boolToByte(values[i])); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + boolean[] values = new boolean[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(buffer)); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + boolean[] values = new boolean[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(stream)); + } + return values; + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(buffer)); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readBoolean(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + if (nullIndicators == null) { + boolean[] values = ColumnEncoder.deserializeBooleanArray(buffer, rowSize); + return new BooleanColumn(rowSize, Optional.empty(), values); + } + + int nonNullCount = 0; + for (boolean isNull : nullIndicators) { + if (!isNull) { + nonNullCount++; + } + } + + boolean[] nonNullValues = ColumnEncoder.deserializeBooleanArray(buffer, nonNullCount); + boolean[] values = new boolean[rowSize]; + int nonNullIndex = 0; + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + values[i] = nonNullValues[nonNullIndex++]; + } + } + return new BooleanColumn(rowSize, Optional.of(nullIndicators), values); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return hasEnoughLength(left, right, rowSize) + && Arrays.equals((boolean[]) left, 0, rowSize, (boolean[]) right, 0, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((boolean[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new BooleanColumn( + positionCount, Optional.of(new boolean[positionCount]), new boolean[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new BooleanColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(BooleanColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new BooleanColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new BooleanColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.BOOLEAN; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DateType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DateType.java index 88d17e242..c231a6f9b 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DateType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DateType.java @@ -19,12 +19,283 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.statistics.DateStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.column.IntColumn; +import org.apache.tsfile.read.common.block.column.IntColumnBuilder; +import org.apache.tsfile.utils.DateUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.utils.TsPrimitiveType.TsInt; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.IntDataPoint; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Optional; + public class DateType extends AbstractIntType { public static final DateType DATE = new DateType(); + private static final LocalDate EMPTY_DATE = LocalDate.of(1000, 1, 1); private DateType() {} + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsInt(TSDataType.DATE); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsInt((int) value, TSDataType.DATE); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsInt(getDateInt(array, rowIndex), TSDataType.DATE); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + if (toArray instanceof int[]) { + super.write(from, toArray, index); + return; + } + ((LocalDate[]) toArray)[index] = DateUtils.parseIntToLocalDate(from.getInt()); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + if (toArray instanceof int[]) { + super.write(from, toArray, index); + return; + } + ((LocalDate[]) toArray)[index] = DateUtils.parseIntToLocalDate(from.getInt()); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new IntDataPoint(measurementId, DateUtils.parseDateExpressionToInt(value)); + } + + @Override + public Statistics createStatistics() { + return new DateStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return DateStatistics.INSTANCE_SIZE; + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + if (array instanceof int[]) { + super.write(writer, time, array, rowIndex, isNull); + return; + } + writer.write( + time, + isNull ? 0 : DateUtils.parseDateExpressionToInt(((LocalDate[]) array)[rowIndex]), + isNull); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + if (column instanceof int[]) { + super.write(writer, time, column, rowIndex); + return; + } + writer.write(time, DateUtils.parseDateExpressionToInt(((LocalDate[]) column)[rowIndex])); + } + + @Override + public void addValue(int rowIndex, Object value, Object array) { + if (array instanceof int[]) { + if (value instanceof Integer) { + ((int[]) array)[rowIndex] = (int) value; + } else { + checkValueType(value, LocalDate.class, "LocalDate"); + ((int[]) array)[rowIndex] = + value != null + ? DateUtils.parseDateExpressionToInt((LocalDate) value) + : DateUtils.EMPTY_DATE_INT; + } + return; + } + checkValueType(value, LocalDate.class, "LocalDate"); + ((LocalDate[]) array)[rowIndex] = value != null ? (LocalDate) value : EMPTY_DATE; + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + if (toArray instanceof int[]) { + super.setTo(from, fromIndex, toArray, toIndex); + return; + } + ((LocalDate[]) toArray)[toIndex] = DateUtils.parseIntToLocalDate(from.getInt(fromIndex)); + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + if (target instanceof int[]) { + ((int[]) target)[targetIndex] = getDateInt(source, sourceIndex); + return; + } + ((LocalDate[]) target)[targetIndex] = + source instanceof int[] + ? DateUtils.parseIntToLocalDate(((int[]) source)[sourceIndex]) + : ((LocalDate[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return array instanceof int[] + ? Arrays.copyOf((int[]) array, newLength) + : Arrays.copyOf((LocalDate[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new LocalDate[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfObjectArray(size); + } + + @Override + public Object getValue(Field field) { + return field.getDateV(); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.DATE); + field.setIntV((int) value); + return field; + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + if (array instanceof int[]) { + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(((int[]) array)[i], stream); + } + return; + } + LocalDate[] values = (LocalDate[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write( + values[i] == null + ? DateUtils.EMPTY_DATE_INT + : DateUtils.parseDateExpressionToInt(values[i]), + stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + if (array instanceof int[]) { + super.serializeArray(array, length, buffer); + return; + } + LocalDate[] values = (LocalDate[]) array; + for (int i = 0; i < length; i++) { + buffer.putInt( + values[i] == null + ? DateUtils.EMPTY_DATE_INT + : DateUtils.parseDateExpressionToInt(values[i])); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + LocalDate[] values = new LocalDate[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = DateUtils.parseIntToLocalDate(ReadWriteIOUtils.readInt(buffer)); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + LocalDate[] values = new LocalDate[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = DateUtils.parseIntToLocalDate(ReadWriteIOUtils.readInt(stream)); + } + return values; + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + return ((IntColumn) super.deserializeColumn(buffer, rowSize, nullIndicators)) + .modifyDataType(TSDataType.DATE); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + if (!hasEnoughLength(left, right, rowSize)) { + return false; + } + if (left instanceof int[] && right instanceof int[]) { + return Arrays.equals((int[]) left, 0, rowSize, (int[]) right, 0, rowSize); + } + if (left instanceof LocalDate[] && right instanceof LocalDate[]) { + return Arrays.equals((LocalDate[]) left, 0, rowSize, (LocalDate[]) right, 0, rowSize); + } + for (int i = 0; i < rowSize; i++) { + if (getDateInt(left, i) != getDateInt(right, i)) { + return false; + } + } + return true; + } + + @Override + public long estimateArraySize(Object array) { + return array instanceof int[] + ? RamUsageEstimator.sizeOf((int[]) array) + : RamUsageEstimator.sizeOf((LocalDate[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new IntColumn( + positionCount, + Optional.of(new boolean[positionCount]), + new int[positionCount], + TSDataType.DATE); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new IntColumn(positionCount, TSDataType.DATE); + } + + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new IntColumnBuilder(columnBuilderStatus, expectedEntries, TSDataType.DATE); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.DATE; @@ -35,6 +306,14 @@ public String getDisplayName() { return "DATE"; } + private int getDateInt(Object array, int index) { + if (array instanceof int[]) { + return ((int[]) array)[index]; + } + LocalDate value = ((LocalDate[]) array)[index]; + return value != null ? DateUtils.parseDateExpressionToInt(value) : DateUtils.EMPTY_DATE_INT; + } + public static DateType getInstance() { return DATE; } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DoubleType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DoubleType.java index 6e325c066..f847fe68c 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DoubleType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/DoubleType.java @@ -21,10 +21,47 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.CamelDecoder; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.DoublePrecisionChimpDecoder; +import org.apache.tsfile.encoding.decoder.DoublePrecisionDecoderV1; +import org.apache.tsfile.encoding.decoder.DoublePrecisionDecoderV2; +import org.apache.tsfile.encoding.decoder.DoubleRLBEDecoder; +import org.apache.tsfile.encoding.decoder.DoubleSprintzDecoder; +import org.apache.tsfile.encoding.decoder.FloatDecoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.DoubleStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.DoubleColumn; import org.apache.tsfile.read.common.block.column.DoubleColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.DoubleDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public class DoubleType extends AbstractType { @@ -32,6 +69,260 @@ public class DoubleType extends AbstractType { private DoubleType() {} + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + BytesUtils.doubleToBytes(value.getDouble(), valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return Double.BYTES; + } + + @Override + public int calcTypeSize(Object value) { + return Double.BYTES; + } + + @Override + public void init(BatchData batchData) { + batchData.initDoubleValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putDouble(timestamp, (double) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + outputStream.writeDouble(batchData.getDoubleByIndex(index)); + } + return Math.multiplyExact(Long.BYTES + Double.BYTES, batchData.length()); + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + stream.writeDouble(value.getDouble()); + return Double.BYTES; + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + buffer.putDouble((double) value); + return Double.BYTES; + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + stream.writeDouble((double) value); + return Double.BYTES; + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getDoubleEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getDoubleCompressor(); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE, TS_2DIFF -> new FloatDecoder(encoding, TSDataType.DOUBLE); + case GORILLA_V1 -> new DoublePrecisionDecoderV1(); + case GORILLA -> new DoublePrecisionDecoderV2(); + case CHIMP -> new DoublePrecisionChimpDecoder(); + case SPRINTZ -> new DoubleSprintzDecoder(); + case RLBE -> new DoubleRLBEDecoder(); + case CAMEL -> new CamelDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Double.BYTES; + } + + @Override + public Statistics createStatistics() { + return new DoubleStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return DoubleStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getDouble()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getDouble()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getDouble(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getDouble(); + } + + @Override + public String toString(Field field) { + return String.valueOf(field.getDoubleV()); + } + + @Override + public Object getValue(Field field) { + return field.getDoubleV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setDoubleV(from.getDouble()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getDoubles()[index] = from.getDouble(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getDoubles(), startIndex, endIndex, (double) value); + } + + @Override + public void setTo(Field from, Field to) { + to.setDoubleV(from.getDoubleV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setDoubleV(from.getDouble()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getDoubles()[index] = from.getDouble(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getDoubles()[toIndex] = from.getDouble(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((double[]) toArray)[toIndex] = from.getDouble(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getDoubleV()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getDouble(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new DoubleDataPoint(measurementId, Double.parseDouble(value)); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new DoubleDataPoint(measurementId, (double) value); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? 0.0D : (double) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getDouble() : Double.MAX_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((double[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? 0 : column.getDouble(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getDoubles(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((double[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getDouble()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getDouble()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (double) value); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.DOUBLE); + field.setDoubleV((double) value); + return field; + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsDouble(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsDouble((double) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsDouble(((double[]) array)[rowIndex]); + } + @Override public int getInt(Column c, int position) { return (int) c.getDouble(position); @@ -52,6 +343,31 @@ public double getDouble(Column c, int position) { return c.getDouble(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeDouble(value.getDouble()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + builder.writeDouble(BytesUtils.bytesToDouble(bytes, offset)); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeDouble(column.getDouble(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((double[]) toArray)[index] = from.getDouble(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((double[]) toArray)[index] = from.getDouble(); + } + @Override public void writeInt(ColumnBuilder builder, int value) { builder.writeDouble(value); @@ -72,11 +388,142 @@ public void writeDouble(ColumnBuilder builder, double value) { builder.writeDouble(value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + checkValueType(value, Double.class, "Double"); + ((double[]) array)[rowIndex] = value != null ? (double) value : Double.MIN_VALUE; + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((double[]) target)[targetIndex] = ((double[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((double[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new double[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfDoubleArray(size); + } + + @Override + public long estimateValueSize() { + return Double.BYTES; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return Math.multiplyExact(Double.BYTES, rowSize); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + double[] values = (double[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(values[i], stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + double[] values = (double[]) array; + for (int i = 0; i < length; i++) { + buffer.putDouble(values[i]); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + double[] values = new double[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readDouble(buffer); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + double[] values = new double[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readDouble(stream); + } + return values; + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readDouble(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readDouble(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + double[] values = new double[rowSize]; + if (nullIndicators == null) { + for (int i = 0; i < rowSize; i++) { + values[i] = Double.longBitsToDouble(buffer.getLong()); + } + } else { + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + values[i] = Double.longBitsToDouble(buffer.getLong()); + } + } + } + return new DoubleColumn(rowSize, Optional.ofNullable(nullIndicators), values); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return hasEnoughLength(left, right, rowSize) + && Arrays.equals((double[]) left, 0, rowSize, (double[]) right, 0, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((double[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new DoubleColumn( + positionCount, Optional.of(new boolean[positionCount]), new double[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new DoubleColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(DoubleColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new DoubleColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new DoubleColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.DOUBLE; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/FloatType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/FloatType.java index 6417f0f52..635ba53ae 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/FloatType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/FloatType.java @@ -21,10 +21,46 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.FloatDecoder; +import org.apache.tsfile.encoding.decoder.FloatRLBEDecoder; +import org.apache.tsfile.encoding.decoder.FloatSprintzDecoder; +import org.apache.tsfile.encoding.decoder.SinglePrecisionChimpDecoder; +import org.apache.tsfile.encoding.decoder.SinglePrecisionDecoderV1; +import org.apache.tsfile.encoding.decoder.SinglePrecisionDecoderV2; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.FloatStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.FloatColumn; import org.apache.tsfile.read.common.block.column.FloatColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.FloatDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; public class FloatType extends AbstractType { @@ -32,6 +68,259 @@ public class FloatType extends AbstractType { private FloatType() {} + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + BytesUtils.floatToBytes(value.getFloat(), valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return Float.BYTES; + } + + @Override + public int calcTypeSize(Object value) { + return Float.BYTES; + } + + @Override + public void init(BatchData batchData) { + batchData.initFloatValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putFloat(timestamp, (float) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + outputStream.writeFloat(batchData.getFloatByIndex(index)); + } + return Math.multiplyExact(Long.BYTES + Float.BYTES, batchData.length()); + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + stream.writeFloat(value.getFloat()); + return Float.BYTES; + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + buffer.putFloat((float) value); + return Float.BYTES; + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + stream.writeFloat((float) value); + return Float.BYTES; + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getFloatEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getFloatCompressor(); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE, TS_2DIFF -> new FloatDecoder(encoding, TSDataType.FLOAT); + case GORILLA_V1 -> new SinglePrecisionDecoderV1(); + case GORILLA -> new SinglePrecisionDecoderV2(); + case CHIMP -> new SinglePrecisionChimpDecoder(); + case SPRINTZ -> new FloatSprintzDecoder(); + case RLBE -> new FloatRLBEDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Float.BYTES; + } + + @Override + public Statistics createStatistics() { + return new FloatStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return FloatStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getFloat()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getFloat()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getFloat(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getFloat(); + } + + @Override + public String toString(Field field) { + return String.valueOf(field.getFloatV()); + } + + @Override + public Object getValue(Field field) { + return field.getFloatV(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setFloatV(from.getFloat()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getFloats()[index] = from.getFloat(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getFloats(), startIndex, endIndex, (float) value); + } + + @Override + public void setTo(Field from, Field to) { + to.setFloatV(from.getFloatV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setFloatV(from.getFloat()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getFloats()[index] = from.getFloat(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getFloats()[toIndex] = from.getFloat(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((float[]) toArray)[toIndex] = from.getFloat(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getFloatV()); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getFloat(columnName)); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new FloatDataPoint(measurementId, Float.parseFloat(value)); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new FloatDataPoint(measurementId, (float) value); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? 0.0F : (float) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getFloat() : Float.MAX_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((float[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + writer.write(time, isNull ? 0 : column.getFloat(rowIndex), isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getFloats(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((float[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getFloat()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getFloat()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (float) value); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.FLOAT); + field.setFloatV((float) value); + return field; + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsFloat(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsFloat((float) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsFloat(((float[]) array)[rowIndex]); + } + @Override public int getInt(Column c, int position) { return (int) c.getFloat(position); @@ -52,6 +341,31 @@ public double getDouble(Column c, int position) { return c.getFloat(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeFloat(value.getFloat()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + builder.writeFloat(BytesUtils.bytesToFloat(bytes, offset)); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeFloat(column.getFloat(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((float[]) toArray)[index] = from.getFloat(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((float[]) toArray)[index] = from.getFloat(); + } + @Override public void writeInt(ColumnBuilder builder, int value) { builder.writeFloat(value); @@ -72,11 +386,142 @@ public void writeDouble(ColumnBuilder builder, double value) { builder.writeFloat((float) value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + checkValueType(value, Float.class, "Float"); + ((float[]) array)[rowIndex] = value != null ? (float) value : Float.MIN_VALUE; + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((float[]) target)[targetIndex] = ((float[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((float[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new float[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfFloatArray(size); + } + + @Override + public long estimateValueSize() { + return Float.BYTES; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return Math.multiplyExact(Float.BYTES, rowSize); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + float[] values = (float[]) array; + for (int i = 0; i < rowSize; i++) { + ReadWriteIOUtils.write(values[i], stream); + } + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + float[] values = (float[]) array; + for (int i = 0; i < length; i++) { + buffer.putFloat(values[i]); + } + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + float[] values = new float[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readFloat(buffer); + } + return values; + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + float[] values = new float[rowSize]; + for (int i = 0; i < rowSize; i++) { + values[i] = ReadWriteIOUtils.readFloat(stream); + } + return values; + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readFloat(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readFloat(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + float[] values = new float[rowSize]; + if (nullIndicators == null) { + for (int i = 0; i < rowSize; i++) { + values[i] = Float.intBitsToFloat(buffer.getInt()); + } + } else { + for (int i = 0; i < rowSize; i++) { + if (!nullIndicators[i]) { + values[i] = Float.intBitsToFloat(buffer.getInt()); + } + } + } + return new FloatColumn(rowSize, Optional.ofNullable(nullIndicators), values); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return hasEnoughLength(left, right, rowSize) + && Arrays.equals((float[]) left, 0, rowSize, (float[]) right, 0, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((float[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new FloatColumn( + positionCount, Optional.of(new boolean[positionCount]), new float[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new FloatColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(FloatColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new FloatColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new FloatColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.FLOAT; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/IntType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/IntType.java index e4d36c2a3..32e958193 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/IntType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/IntType.java @@ -19,12 +19,22 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.Field; + public class IntType extends AbstractIntType { public static final IntType INT32 = new IntType(); private IntType() {} + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.INT32); + field.setIntV((int) value); + return field; + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.INT32; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/LongType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/LongType.java index a1ed2a1c9..77c3d0520 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/LongType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/LongType.java @@ -19,12 +19,22 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.Field; + public class LongType extends AbstractLongType { public static final LongType INT64 = new LongType(); private LongType() {} + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.INT64); + field.setLongV((long) value); + return field; + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.INT64; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/ObjectType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/ObjectType.java index b30b621f1..1025d55da 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/ObjectType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/ObjectType.java @@ -21,11 +21,44 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.ObjectStatistics; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.column.BinaryColumn; import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.read.query.dataset.ResultSet; import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.utils.TsPrimitiveType.TsBinary; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.StringDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; + +import static org.apache.tsfile.utils.BytesUtils.parseObjectByteArrayToString; public class ObjectType extends AbstractType { @@ -33,21 +66,406 @@ public class ObjectType extends AbstractType { private ObjectType() {} + @Override + public void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + binaryToBytes(value, valueBytes, offset); + } + + @Override + public int calcTypeSize(TsPrimitiveType value) { + return binaryTypeSize(value); + } + + @Override + public int calcTypeSize(Object value) { + return binaryTypeSize(value); + } + + @Override + public void init(BatchData batchData) { + batchData.initBinaryValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putBinary(timestamp, (Binary) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + int size = 0; + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + Binary binary = batchData.getBinaryByIndex(index); + outputStream.writeInt(binary.getLength()); + outputStream.write(binary.getValues()); + size = Math.addExact(size, Long.BYTES + Integer.BYTES); + size = Math.addExact(size, binary.getLength()); + } + return size; + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + Binary binary = value.getBinary(); + stream.writeInt(binary.getLength()); + stream.write(binary.getValues()); + return Math.addExact(Integer.BYTES, binary.getLength()); + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + return ReadWriteIOUtils.write((Binary) value, buffer); + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) throws IOException { + return ReadWriteIOUtils.write((Binary) value, stream); + } + + @Override + public TSEncoding getDefaultEncoding(TSFileConfig config) { + return TSEncoding.valueOf(config.getTextEncoding()); + } + + @Override + public CompressionType getDefaultCompressor(TSFileConfig config) { + return config.getTextCompressor(); + } + + @Override + public int getOneItemMaxSize(int valveLength) { + return Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * valveLength; + } + + @Override + public Statistics createStatistics() { + return new ObjectStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return ObjectStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + stats.update(timestamp, value.getBinary()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + stats.update(batchData.currentTime(), batchData.getBinary()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + stats.update(block.getTimeByIndex(rowIndex), block.getColumn(columnIndex).getBinary(rowIndex)); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getBinary(); + } + + @Override + public String toString(Field field) { + return parseObjectByteArrayToString(field.getBinaryV().getValues()); + } + + @Override + public Object getValue(Field field) { + return field.getStringValue(); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(TsPrimitiveType from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Object value, Column to, int startIndex, int endIndex) { + Arrays.fill(to.getBinaries(), startIndex, endIndex, value); + } + + @Override + public void setTo(Field from, Field to) { + to.setBinaryV(from.getBinaryV()); + } + + @Override + public void setTo(BatchData from, Field to) { + to.setBinaryV(from.getBinary()); + } + + @Override + public void setTo(BatchData from, Column to, int index) { + to.getBinaries()[index] = from.getBinary(); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + to.getBinaries()[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((Binary[]) toArray)[toIndex] = from.getBinary(fromIndex); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + record.addPoint(columnName, resultSet.getBinary(columnName)); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + record.addPoint(columnName, field.getBinaryV().getValues()); + } + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + return new StringDataPoint(measurementId, new Binary(value, TSFileConfig.STRING_CHARSET)); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + writer.write(time, isNull ? Binary.EMPTY_VALUE : (Binary) value, isNull); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + writer.write(time, value != null ? value.getBinary() : Binary.EMPTY_VALUE, value == null); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + writer.write(time, ((Binary[]) array)[rowIndex], isNull); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + writer.write(times, column.getBinaries(), column.isNull(), batchSize, arrayOffset); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + writer.write(time, ((Binary[]) column)[rowIndex]); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + writer.write(time, value.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + writer.write(time, data.getBinary()); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + writer.write(time, (Binary) value); + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.OBJECT); + field.setBinaryV((Binary) value); + return field; + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsBinary(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsBinary((Binary) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsBinary(((Binary[]) array)[rowIndex]); + } + @Override public Binary getBinary(Column c, int position) { return c.getBinary(position); } + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + builder.writeBinary(value.getBinary()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + int length = BytesUtils.bytesToInt(bytes, offset); + builder.writeBinary( + new Binary( + Arrays.copyOfRange(bytes, offset + Integer.BYTES, offset + Integer.BYTES + length))); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + builder.writeBinary(column.getBinary(index)); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((Binary[]) toArray)[index] = from.getBinary(); + } + @Override public void writeBinary(ColumnBuilder builder, Binary value) { builder.writeBinary(value); } + @Override + public void addValue(int rowIndex, Object value, Object array) { + if (value != null && !(value instanceof Binary) && !(value instanceof String)) { + throw new IllegalArgumentException( + Messages.format( + "error.write.tablet_expected_type", + "Binary or String", + getDisplayName(), + value.getClass().getName())); + } + if (value instanceof Binary) { + ((Binary[]) array)[rowIndex] = (Binary) value; + } else { + ((Binary[]) array)[rowIndex] = + value != null + ? new Binary(((String) value).getBytes(TSFileConfig.STRING_CHARSET)) + : Binary.EMPTY_VALUE; + } + } + + @Override + public void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + ((Binary[]) target)[targetIndex] = ((Binary[]) source)[sourceIndex]; + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((Binary[]) array, newLength); + } + + @Override + public Object createArray(int capacity) { + return new Binary[capacity]; + } + + @Override + public long estimateArraySize(int size) { + return RamUsageEstimator.sizeOfObjectArray(size); + } + + @Override + public long estimateValueSize() { + return RamUsageEstimator.NUM_BYTES_OBJECT_REF; + } + + @Override + public int serializedSize(Object array, int rowSize) { + return serializedSizeOfBinaryValues(array, rowSize); + } + + @Override + public int serializedSize(Object array, int startRow, int endRow) { + return serializedSizeOfBinaryValues(array, startRow, endRow); + } + + @Override + public void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + serializeBinaryValues(array, rowSize, stream); + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + serializeBinaryValues(array, length, buffer); + } + + @Override + public Object deserializeArray(ByteBuffer buffer, int rowSize) { + return deserializeBinaryValues(buffer, rowSize); + } + + @Override + public Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + return deserializeBinaryValues(stream, rowSize); + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + array[index] = ReadWriteIOUtils.readBinary(buffer); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) throws IOException { + array[index] = ReadWriteIOUtils.readBinary(stream); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + Binary[] values = deserializeBinaryValues(buffer, rowSize, nullIndicators); + return new BinaryColumn(0, rowSize, nullIndicators, values); + } + + @Override + public boolean arrayEquals(Object left, Object right, int rowSize) { + return binaryArrayEquals(left, right, rowSize); + } + + @Override + public long estimateArraySize(Object array) { + return RamUsageEstimator.sizeOf((Binary[]) array); + } + + @Override + public Column createColumnWithMaxPosition(int positionCount) { + return new BinaryColumn( + positionCount, Optional.of(new boolean[positionCount]), new Binary[positionCount]); + } + + @Override + public Column createColumnWithZeroPosition(int positionCount) { + return new BinaryColumn(positionCount); + } + + @Override + public Column createNullColumn(int positionCount) { + return new RunLengthEncodedColumn(BinaryColumnBuilder.NULL_VALUE_BLOCK, positionCount); + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new BinaryColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + return new BinaryColumnBuilder(columnBuilderStatus, expectedEntries); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.OBJECT; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/RowType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/RowType.java index 96bd3b380..6123fb30b 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/RowType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/RowType.java @@ -20,6 +20,8 @@ package org.apache.tsfile.read.common.type; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.utils.TsPrimitiveType; import java.util.ArrayList; import java.util.Arrays; @@ -73,6 +75,31 @@ public static RowType createWithTypeSignature(List fields) { return new RowType(fields); } + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsVector(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsVector((TsPrimitiveType[]) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsVector(((TsPrimitiveType[][]) array)[rowIndex]); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((TsPrimitiveType[][]) toArray)[index] = from.getVector(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((TsPrimitiveType[][]) toArray)[index] = from.getVector(); + } + public static Field field(String name, Type type) { return new Field(Optional.of(name), type); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/StringType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/StringType.java index 725ae9e3b..f5d54ed7d 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/StringType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/StringType.java @@ -19,12 +19,35 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.file.metadata.statistics.StringStatistics; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.utils.Binary; + public class StringType extends AbstractVarcharType { public static final StringType STRING = new StringType(); private StringType() {} + @Override + public Statistics createStatistics() { + return new StringStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return StringStatistics.INSTANCE_SIZE; + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.STRING); + field.setBinaryV((Binary) value); + return field; + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.STRING; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TimestampType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TimestampType.java index c4e5e8434..143c577f0 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TimestampType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TimestampType.java @@ -19,12 +19,34 @@ package org.apache.tsfile.read.common.type; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.file.metadata.statistics.TimestampStatistics; +import org.apache.tsfile.read.common.Field; + public class TimestampType extends AbstractLongType { public static final TimestampType TIMESTAMP = new TimestampType(); private TimestampType() {} + @Override + public Statistics createStatistics() { + return new TimestampStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return TimestampStatistics.INSTANCE_SIZE; + } + + @Override + public Field getField(Object value) { + Field field = new Field(TSDataType.TIMESTAMP); + field.setLongV((long) value); + return field; + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.TIMESTAMP; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/Type.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/Type.java index cb6b60944..d7be3dbab 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/Type.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/Type.java @@ -21,12 +21,48 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.exception.write.UnknownColumnTypeException; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.query.dataset.ResultSet; import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.StringDataPoint; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; import java.util.List; +/** + * Type provides common interfaces associated with a specific TsDataType to remove uses of + * "switch(dataType)", which make the extension of datatypes very difficult. + * + *

Interfaces that are less generic should be defined using TypeService and checked in a proper + * context. + */ public interface Type { + static Type fromTsDataType(TSDataType tsDataType) { + return TypeFactory.getType(tsDataType); + } + /** Gets a boolean at {@code position}. */ default boolean getBoolean(Column c, int position) { throw new UnsupportedOperationException(getClass().getName()); @@ -62,6 +98,89 @@ default Object getObject(Column c, int position) { return c.getObject(position); } + /** Creates an empty TsPrimitiveType for this type. */ + default TsPrimitiveType getTsPrimitiveType() { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Creates a TsPrimitiveType initialized with {@code value}. */ + default TsPrimitiveType getTsPrimitiveType(Object value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Serializes {@code value} into {@code valueBytes} starting at {@code offset}. */ + default void toBytes(TsPrimitiveType value, byte[] valueBytes, int offset) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the number of bytes required to serialize {@code value}. */ + default int calcTypeSize(TsPrimitiveType value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the number of bytes required to serialize {@code value}. */ + default int calcTypeSize(Object value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Initializes the value storage in {@code batchData} for this type. */ + default void init(BatchData batchData) { + throw new UnSupportedDataTypeException(String.valueOf(batchData.getDataType())); + } + + /** Adds a timestamp-value pair to {@code batchData}. */ + default void put(BatchData batchData, long timestamp, Object value) { + throw new UnSupportedDataTypeException(String.valueOf(batchData.getDataType())); + } + + /** Serializes all timestamp-value pairs in {@code batchData} and returns the serialized size. */ + default int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + throw new IllegalArgumentException( + Messages.format("error.read.batch_data_unknown_type", batchData.getDataType())); + } + + /** Serializes {@code value} to {@code stream} and returns the serialized size. */ + default int serialize(TsPrimitiveType value, DataOutputStream stream) throws IOException { + throw new IllegalArgumentException( + Messages.format("error.read.batch_data_unknown_type", value.getDataType())); + } + + /** Serializes {@code value} to {@code buffer} and returns the serialized size. */ + default int serializeValue(Object value, ByteBuffer buffer) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Serializes {@code value} to {@code stream} and returns the serialized size. */ + default int serializeValue(Object value, DataOutputStream stream) throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Writes a TsPrimitiveType value to the current entry. */ + default void write(ColumnBuilder builder, TsPrimitiveType value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Writes the value serialized in {@code bytes} at {@code offset} to the current entry. */ + default void write(ColumnBuilder builder, byte[] bytes, int offset) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Writes the value at {@code index} in {@code column} to the current entry. */ + default void write(ColumnBuilder builder, Column column, int index) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Writes a TsPrimitiveType value to {@code array} at {@code index}. */ + default void write(TsPrimitiveType from, Object toArray, int index) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Writes the current value from {@code from} to {@code toArray} at {@code index}. */ + default void write(BatchData from, Object toArray, int index) { + throw new UnsupportedOperationException(getClass().getName()); + } + /** Write a boolean to the current entry; */ default void writeBoolean(ColumnBuilder builder, boolean value) { throw new UnsupportedOperationException(getClass().getName()); @@ -97,12 +216,323 @@ default void writeObject(ColumnBuilder builder, Object value) { builder.writeObject(value); } + /** Adds a value to the array at {@code rowIndex}. */ + default void addValue(int rowIndex, Object value, Object array) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Copies one element from {@code source} to {@code target}. */ + default void copyArrayElement(Object source, int sourceIndex, Object target, int targetIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Copies {@code array}, truncating or padding it to {@code newLength}. */ + default Object arrayCopyOf(Object array, int newLength) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Creates an array column with {@code capacity} entries for this type. */ + default Object createArray(int capacity) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Estimates the retained memory size of an array with {@code size} entries. */ + default long estimateArraySize(int size) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Estimates the retained memory size of {@code array}. */ + default long estimateArraySize(Object array) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Estimates the retained memory size of a value of this type. */ + default long estimateValueSize() { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the serialized byte size of the array with {@code rowSize} entries. */ + default int serializedSize(Object array, int rowSize) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the serialized byte size of the array within the given range. */ + default int serializedSize(Object array, int startRow, int endRow) { + return serializedSize(array, endRow - startRow); + } + + /** Serializes the array column with {@code rowSize} entries. */ + default void serializeArray(Object array, int rowSize, DataOutputStream stream) + throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Serializes the first {@code length} entries in {@code array} to {@code buffer}. */ + default void serializeArray(Object array, int length, ByteBuffer buffer) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Deserializes an array with {@code rowSize} entries from {@code buffer}. */ + default Object deserializeArray(ByteBuffer buffer, int rowSize) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Deserializes an array with {@code rowSize} entries from {@code stream}. */ + default Object deserializeArray(DataInputStream stream, int rowSize) throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Deserializes one value from {@code buffer} into {@code array} at {@code index}. */ + default void deserialize(Object[] array, int index, ByteBuffer buffer) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Deserializes one value from {@code stream} into {@code array} at {@code index}. */ + default void deserialize(Object[] array, int index, InputStream stream) throws IOException { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Deserializes an array with {@code rowSize} entries from {@code buffer}. */ + default Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns whether two array columns are equal in the first {@code rowSize} entries. */ + default boolean arrayEquals(Object left, Object right, int rowSize) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the value at {@code rowIndex} from an array. */ + default Object getValue(Object array, int rowIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the value at {@code rowIndex} from an array as a TsPrimitiveType. */ + default TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the value in {@code field}. */ + default Object getValue(Field field) { + throw new UnSupportedDataTypeException(getTypeEnum().toString()); + } + + /** Creates a field initialized with {@code value}. */ + default Field getField(Object value) { + throw new UnSupportedDataTypeException(getTypeEnum().toString()); + } + + /** Copies the value from {@code from} to {@code to}. */ + default void setTo(TsPrimitiveType from, Field to) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + /** Copies the value from {@code from} to {@code to} at {@code index}. */ + default void setTo(TsPrimitiveType from, Column to, int index) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + /** Copies {@code value} to {@code to} from {@code startIndex} to {@code endIndex}. */ + default void setTo(Object value, Column to, int startIndex, int endIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Copies the value from {@code from} to {@code to}. */ + default void setTo(Field from, Field to) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + /** Copies the current value from {@code from} to {@code to}. */ + default void setTo(BatchData from, Field to) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + /** Copies the current value from {@code from} to {@code to} at {@code index}. */ + default void setTo(BatchData from, Column to, int index) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + /** Copies a value from {@code from} at {@code fromIndex} to {@code to} at {@code toIndex}. */ + default void setTo(Column from, int fromIndex, Column to, int toIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * Copies a value from {@code from} at {@code fromIndex} to {@code toArray} at {@code toIndex}. + */ + default void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Adds the value of {@code columnName} in {@code resultSet} to {@code record}. */ + default void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", this.getTypeEnum())); + } + + /** Adds the value in {@code field} to {@code record}. */ + default void addPoint(TSRecord record, String columnName, Field field) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", this.getTypeEnum())); + } + + /** Creates a data point by parsing a string value. */ + default DataPoint getDataPoint(String measurementId, String value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Creates a data point from a long value for test file generation. */ + default DataPoint getDataPoint(String measurementId, long value) { + return new StringDataPoint( + measurementId, new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET)); + } + + /** Writes a value to an aligned value chunk. */ + default void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a TsPrimitiveType value to an aligned value chunk. */ + default void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a value at {@code rowIndex} in a Tablet column to an aligned value chunk. */ + default void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a value at {@code rowIndex} in a TsBlock column to an aligned value chunk. */ + default void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a batch of values in a TsBlock column to an aligned value chunk. */ + default void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + throw new UnsupportedOperationException( + Messages.format("error.write.chunk_unknown_type", getTypeEnum())); + } + + /** Writes a value at {@code rowIndex} in a Tablet column to a non-aligned chunk. */ + default void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a TsPrimitiveType value to a non-aligned chunk. */ + default void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes the current value in {@code data} to a non-aligned chunk. */ + default void write(ChunkWriterImpl writer, long time, BatchData data) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Writes a value to a non-aligned chunk. */ + default void write(ChunkWriterImpl writer, long time, Object value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + /** Creates a column with {@code positionCount} capacity for this type. */ + default Column createColumnWithMaxPosition(int positionCount) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Creates a column with {@code positionCount} capacity for this type. */ + default Column createColumnWithZeroPosition(int positionCount) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Creates an all-null column with {@code positionCount} positions. */ + default Column createNullColumn(int positionCount) { + throw new IllegalArgumentException( + Messages.format("error.read.null_col_unknown_type", getTypeEnum())); + } + + /** Returns the default encoding configured for this type. */ + default TSEncoding getDefaultEncoding(TSFileConfig config) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the default compressor configured for this type. */ + default CompressionType getDefaultCompressor(TSFileConfig config) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns a decoder for the specified encoding. */ + default Decoder getDecoder(TSEncoding encoding) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the maximum encoded size of one item. */ + default int getOneItemMaxSize(int valveLength) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Creates empty statistics for this type. */ + default Statistics createStatistics() { + throw new UnknownColumnTypeException(getTypeEnum().toString()); + } + + /** Returns the estimated instance size of statistics for this type. */ + default long getStatisticsInstanceSize() { + throw new UnknownColumnTypeException(getTypeEnum().toString()); + } + + /** Updates statistics with a timestamp-value pair. */ + default void update(Statistics stats, long timestamp, TsPrimitiveType value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Updates statistics with the current timestamp-value pair in {@code batchData}. */ + default void update(Statistics stats, BatchData batchData) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Updates statistics with the value at {@code rowIndex} in a TsBlock column. */ + default void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the current value in {@code batchData}. */ + default Object getCurrentValue(BatchData batchData) { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** Returns the string representation of the value in {@code field}. */ + default String toString(Field field) { + throw new UnSupportedDataTypeException(field.getDataType().toString()); + } + /** * Creates the preferred column builder for this type. This is the builder used to store values * after an expression projection within the read. */ ColumnBuilder createColumnBuilder(int expectedEntries); + /** Creates the preferred column builder for this type with memory tracking. */ + default ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + throw new UnsupportedOperationException(getClass().getName()); + } + TypeEnum getTypeEnum(); /** Returns the name of this type that should be displayed to end-users. */ diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeEnum.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeEnum.java index 52db17a39..8bcab3d10 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeEnum.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeEnum.java @@ -39,5 +39,6 @@ public enum TypeEnum { DATE, BLOB, STRING, - OBJECT + OBJECT, + VECTOR } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeFactory.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeFactory.java index bebefbdea..7e4d542f0 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeFactory.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/TypeFactory.java @@ -29,62 +29,43 @@ private TypeFactory() { } public static Type getType(TSDataType tsDataType) { - switch (tsDataType) { - case DATE: - return DateType.getInstance(); - case INT32: - return IntType.getInstance(); - case INT64: - return LongType.getInstance(); - case TIMESTAMP: - return TimestampType.getInstance(); - case FLOAT: - return FloatType.getInstance(); - case DOUBLE: - return DoubleType.getInstance(); - case BOOLEAN: - return BooleanType.getInstance(); - case TEXT: - return BinaryType.getInstance(); - case STRING: - return StringType.getInstance(); - case BLOB: - return BlobType.getInstance(); - case OBJECT: - return ObjectType.getInstance(); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.typefactory_invalid_tsdata_type", tsDataType)); - } + return switch (tsDataType) { + case DATE -> DateType.getInstance(); + case INT32 -> IntType.getInstance(); + case INT64 -> LongType.getInstance(); + case TIMESTAMP -> TimestampType.getInstance(); + case FLOAT -> FloatType.getInstance(); + case DOUBLE -> DoubleType.getInstance(); + case BOOLEAN -> BooleanType.getInstance(); + case TEXT -> BinaryType.getInstance(); + case STRING -> StringType.getInstance(); + case BLOB -> BlobType.getInstance(); + case OBJECT -> ObjectType.getInstance(); + case VECTOR -> VectorType.getInstance(); + case UNKNOWN -> UnknownType.getInstance(); + default -> + throw new UnsupportedOperationException( + Messages.format("error.read.typefactory_invalid_tsdata_type", tsDataType)); + }; } public static Type getType(TypeEnum typeEnum) { - switch (typeEnum) { - case INT32: - return IntType.getInstance(); - case INT64: - return LongType.getInstance(); - case FLOAT: - return FloatType.getInstance(); - case DOUBLE: - return DoubleType.getInstance(); - case BOOLEAN: - return BooleanType.getInstance(); - case TEXT: - return BinaryType.getInstance(); - case UNKNOWN: - return UnknownType.getInstance(); - case DATE: - return DateType.getInstance(); - case TIMESTAMP: - return TimestampType.getInstance(); - case BLOB: - return BlobType.getInstance(); - case STRING: - return StringType.getInstance(); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.typefactory_invalid_type_enum", typeEnum)); - } + return switch (typeEnum) { + case INT32 -> IntType.getInstance(); + case INT64 -> LongType.getInstance(); + case FLOAT -> FloatType.getInstance(); + case DOUBLE -> DoubleType.getInstance(); + case BOOLEAN -> BooleanType.getInstance(); + case TEXT -> BinaryType.getInstance(); + case UNKNOWN -> UnknownType.getInstance(); + case DATE -> DateType.getInstance(); + case TIMESTAMP -> TimestampType.getInstance(); + case BLOB -> BlobType.getInstance(); + case STRING -> StringType.getInstance(); + case VECTOR -> VectorType.getInstance(); + default -> + throw new UnsupportedOperationException( + Messages.format("error.read.typefactory_invalid_type_enum", typeEnum)); + }; } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/UnknownType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/UnknownType.java index 802165dc8..4df624811 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/UnknownType.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/UnknownType.java @@ -21,6 +21,9 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.block.column.BooleanColumnBuilder; import java.util.Collections; @@ -51,11 +54,23 @@ public boolean getBoolean(Column column, int position) { return false; } + @Override + public Object getCurrentValue(BatchData batchData) { + return null; + } + @Override public ColumnBuilder createColumnBuilder(int expectedEntries) { return new BooleanColumnBuilder(null, expectedEntries); } + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + throw new IllegalArgumentException( + Messages.format("error.read.tsblock_builder_unknown_type", getTypeEnum())); + } + @Override public TypeEnum getTypeEnum() { return TypeEnum.UNKNOWN; diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/VectorType.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/VectorType.java new file mode 100644 index 000000000..680900af4 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/VectorType.java @@ -0,0 +1,377 @@ +/* + * 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.tsfile.read.common.type; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.block.column.ColumnBuilderStatus; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.encoding.decoder.DeltaBinaryDecoder; +import org.apache.tsfile.encoding.decoder.LongChimpDecoder; +import org.apache.tsfile.encoding.decoder.LongGorillaDecoder; +import org.apache.tsfile.encoding.decoder.LongRleDecoder; +import org.apache.tsfile.encoding.decoder.RegularDataDecoder; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.file.metadata.statistics.TimeStatistics; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; +import org.apache.tsfile.write.record.datapoint.StringDataPoint; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class VectorType extends AbstractLongType { + + public static final VectorType VECTOR = new VectorType(); + + private VectorType() {} + + @Override + public DataPoint getDataPoint(String measurementId, String value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public DataPoint getDataPoint(String measurementId, long value) { + return new StringDataPoint( + measurementId, new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET)); + } + + @Override + public void write(ValueChunkWriter writer, long time, Object value, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write(ValueChunkWriter writer, long time, TsPrimitiveType value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write(ColumnBuilder builder, TsPrimitiveType value) { + throw new UnsupportedOperationException(getClass().getName()); + } + + @Override + public void write(ColumnBuilder builder, byte[] bytes, int offset) { + throw new UnsupportedOperationException(getClass().getName()); + } + + @Override + public void write(ColumnBuilder builder, Column column, int index) { + throw new UnsupportedOperationException(getClass().getName()); + } + + @Override + public void write(TsPrimitiveType from, Object toArray, int index) { + ((TsPrimitiveType[][]) toArray)[index] = from.getVector(); + } + + @Override + public void write(BatchData from, Object toArray, int index) { + ((TsPrimitiveType[][]) toArray)[index] = from.getVector(); + } + + @Override + public Object arrayCopyOf(Object array, int newLength) { + return Arrays.copyOf((long[]) array, newLength); + } + + @Override + public void setTo(Column from, int fromIndex, Object toArray, int toIndex) { + ((long[]) toArray)[toIndex] = from.getLong(fromIndex); + } + + @Override + public void setTo(Column from, int fromIndex, Column to, int toIndex) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Object array, int rowIndex, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write( + ValueChunkWriter writer, long time, Column column, int rowIndex, boolean isNull) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write( + ValueChunkWriter writer, long[] times, Column column, int batchSize, int arrayOffset) { + throw new UnsupportedOperationException( + Messages.format("error.write.chunk_unknown_type", getTypeEnum())); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object column, int rowIndex) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write(ChunkWriterImpl writer, long time, TsPrimitiveType value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write(ChunkWriterImpl writer, long time, BatchData data) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public void write(ChunkWriterImpl writer, long time, Object value) { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", getTypeEnum())); + } + + @Override + public ColumnBuilder createColumnBuilder( + ColumnBuilderStatus columnBuilderStatus, int expectedEntries) { + throw new IllegalArgumentException( + Messages.format("error.read.tsblock_builder_unknown_type", getTypeEnum())); + } + + @Override + public void init(BatchData batchData) { + batchData.initVectorValues(); + } + + @Override + public void put(BatchData batchData, long timestamp, Object value) { + batchData.putVector(timestamp, (TsPrimitiveType[]) value); + } + + @Override + public int serialize(BatchData batchData, DataOutputStream outputStream, boolean isDesc) + throws IOException { + int size = 0; + for (int i = 0; i < batchData.length(); i++) { + int index = isDesc ? batchData.length() - 1 - i : i; + outputStream.writeLong(batchData.getTimeByIndex(index)); + TsPrimitiveType[] values = batchData.getVectorByIndex(index); + outputStream.writeInt(values.length); + size = Math.addExact(size, Long.BYTES + Integer.BYTES); + for (TsPrimitiveType value : values) { + size = Math.addExact(size, Byte.BYTES); + if (value == null) { + outputStream.write(0); + continue; + } + outputStream.write(1); + outputStream.write(value.getDataType().serialize()); + size = Math.addExact(size, Byte.BYTES); + size = + Math.addExact( + size, Type.fromTsDataType(value.getDataType()).serialize(value, outputStream)); + } + } + return size; + } + + @Override + public int serialize(TsPrimitiveType value, DataOutputStream stream) { + throw new IllegalArgumentException( + Messages.format("error.read.batch_data_unknown_type", value.getDataType())); + } + + @Override + public int serializeValue(Object value, ByteBuffer buffer) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public int serializeValue(Object value, DataOutputStream stream) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void serializeArray(Object array, int length, ByteBuffer buffer) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public Decoder getDecoder(TSEncoding encoding) { + return switch (encoding) { + case PLAIN, DICTIONARY -> super.getDecoder(encoding); + case RLE -> new LongRleDecoder(); + case TS_2DIFF -> new DeltaBinaryDecoder.LongDeltaDecoder(); + case REGULAR -> new RegularDataDecoder.LongRegularDecoder(); + case GORILLA -> new LongGorillaDecoder(); + case CHIMP -> new LongChimpDecoder(); + default -> throw decoderNotFound(encoding); + }; + } + + @Override + public int getOneItemMaxSize(int valveLength) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public Statistics createStatistics() { + return new TimeStatistics(); + } + + @Override + public long getStatisticsInstanceSize() { + return TimeStatistics.INSTANCE_SIZE; + } + + @Override + public void update(Statistics stats, long timestamp, TsPrimitiveType value) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void update(Statistics stats, BatchData batchData) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void update(Statistics stats, TsBlock block, int columnIndex, int rowIndex) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public Object getCurrentValue(BatchData batchData) { + return batchData.getVector(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType() { + return new TsPrimitiveType.TsVector(); + } + + @Override + public TsPrimitiveType getTsPrimitiveType(Object value) { + return new TsPrimitiveType.TsVector((TsPrimitiveType[]) value); + } + + @Override + public TsPrimitiveType getValueAsTsPrimitiveType(Object array, int rowIndex) { + return new TsPrimitiveType.TsVector(((TsPrimitiveType[][]) array)[rowIndex]); + } + + @Override + public void addPoint(TSRecord record, String columnName, ResultSet resultSet) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", getTypeEnum())); + } + + @Override + public void addPoint(TSRecord record, String columnName, Field field) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", getTypeEnum())); + } + + @Override + public Object deserializeColumn(ByteBuffer buffer, int rowSize, boolean[] nullIndicators) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void deserialize(Object[] array, int index, ByteBuffer buffer) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public void deserialize(Object[] array, int index, InputStream stream) { + throw new UnsupportedOperationException(getDisplayName()); + } + + @Override + public Column createNullColumn(int positionCount) { + throw new IllegalArgumentException( + Messages.format("error.read.null_col_unknown_type", getTypeEnum())); + } + + @Override + public String toString(Field field) { + throw new UnSupportedDataTypeException(field.getDataType().toString()); + } + + @Override + public Object getValue(Field field) { + throw new UnSupportedDataTypeException(getTypeEnum().toString()); + } + + @Override + public Field getField(Object value) { + throw new UnSupportedDataTypeException(getTypeEnum().toString()); + } + + @Override + public void setTo(TsPrimitiveType from, Field to) { + throw new UnSupportedDataTypeException( + Messages.format("error.common.unsupported_data_type", from.getDataType())); + } + + @Override + public void setTo(BatchData from, Field to) { + TsPrimitiveType value = from.getVector()[0]; + Type.fromTsDataType(value.getDataType()).setTo(value, to); + } + + @Override + public long estimateValueSize() { + return RamUsageEstimator.NUM_BYTES_OBJECT_REF; + } + + @Override + public TypeEnum getTypeEnum() { + return TypeEnum.VECTOR; + } + + @Override + public String getDisplayName() { + return "VECTOR"; + } + + public static VectorType getInstance() { + return VECTOR; + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/BooleanTypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/BooleanTypeService.java new file mode 100644 index 000000000..22e3ce7fa --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/BooleanTypeService.java @@ -0,0 +1,49 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; + +/** + * A BooleanTypeService wraps a switch(dataType) clause into a checked service class. + * Implementations should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

It returns boolean only. + */ +public interface BooleanTypeService { + + boolean call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + call(type); + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/DoubleTypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/DoubleTypeService.java new file mode 100644 index 000000000..7f3af905d --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/DoubleTypeService.java @@ -0,0 +1,49 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; + +/** + * A DoubleTypeService wraps a switch(dataType) clause into a checked service class. Implementations + * should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

It returns double only. + */ +public interface DoubleTypeService { + + double call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + call(type); + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/FloatTypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/FloatTypeService.java new file mode 100644 index 000000000..2d19f6abc --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/FloatTypeService.java @@ -0,0 +1,49 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; + +/** + * A FloatTypeService wraps a switch(dataType) clause into a checked service class. Implementations + * should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

It returns float only. + */ +public interface FloatTypeService { + + float call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + call(type); + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/IntTypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/IntTypeService.java new file mode 100644 index 000000000..e0900b299 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/IntTypeService.java @@ -0,0 +1,49 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; + +/** + * An IntTypeService wraps a switch(dataType) clause into a checked service class. Implementations + * should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

It returns int only. + */ +public interface IntTypeService { + + int call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + call(type); + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/LongTypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/LongTypeService.java new file mode 100644 index 000000000..c589fb060 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/LongTypeService.java @@ -0,0 +1,49 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; + +/** + * A LongTypeService wraps a switch(dataType) clause into a checked service class. Implementations + * should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

It returns long only. + */ +public interface LongTypeService { + + long call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + call(type); + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/TypeService.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/TypeService.java new file mode 100644 index 000000000..7c4b28615 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/type/service/TypeService.java @@ -0,0 +1,61 @@ +/* + * 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.tsfile.read.common.type.service; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.write.UnSupportedDataTypeException; + +/** + * A TypeService wraps a switch(dataType) clause into a checked service class. Implementations + * should cover all Types that can be instanced from TsDataTypes, and throw + * UnsupportedDataTypeException when an unrecognized type is encountered. + * + *

Use enhanced-switch and list all branches in the implementation to expose uncovered datatypes + * during compilation. + * + * @param the return type of the service. + */ +public interface TypeService { + + T call(Type type); + + default void check() { + for (TSDataType tsDataType : TSDataType.values()) { + Type type = null; + try { + type = Type.fromTsDataType(tsDataType); + } catch (UnsupportedOperationException e) { + // ignore types that cannot be instanced + } + + if (type != null) { + try { + call(type); + } catch (UnSupportedDataTypeException e) { + if (!e.isChecked()) { + // the exception is caused by an uncovered datatype, throw it to fail any tests + // involving this service + throw e; + } + } + } + } + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/ValueFilterApi.java b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/ValueFilterApi.java index 4423792f5..2b25915d5 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/ValueFilterApi.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/ValueFilterApi.java @@ -21,20 +21,12 @@ import org.apache.tsfile.common.regexp.LikePattern; import org.apache.tsfile.enums.TSDataType; -import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.filter.basic.Filter; -import org.apache.tsfile.read.filter.operator.BinaryFilterOperators; -import org.apache.tsfile.read.filter.operator.BooleanFilterOperators; -import org.apache.tsfile.read.filter.operator.DoubleFilterOperators; import org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators; import org.apache.tsfile.read.filter.operator.ExtractValueFilterOperators; -import org.apache.tsfile.read.filter.operator.FloatFilterOperators; -import org.apache.tsfile.read.filter.operator.IntegerFilterOperators; -import org.apache.tsfile.read.filter.operator.LongFilterOperators; -import org.apache.tsfile.read.filter.operator.StringFilterOperators; import org.apache.tsfile.read.filter.operator.ValueIsNotNullOperator; import org.apache.tsfile.read.filter.operator.ValueIsNullOperator; -import org.apache.tsfile.utils.Binary; import java.time.ZoneId; import java.util.Objects; @@ -42,6 +34,21 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_BETWEEN_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_EQ_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_GT_EQ_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_GT_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_IN_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_LIKE_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_LT_EQ_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_LT_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_NOT_BETWEEN_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_NOT_EQ_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_NOT_IN_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_NOT_LIKE_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_NOT_REGEXP_FILTER_SERVICE; +import static org.apache.tsfile.utils.ValueFilterTypeServices.VALUE_REGEXP_FILTER_SERVICE; + public class ValueFilterApi { public static final int DEFAULT_MEASUREMENT_INDEX = 0; @@ -56,166 +63,43 @@ private ValueFilterApi() { public static Filter gt(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueGt(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueGt(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueGt(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueGt(measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueGt(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueGt(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueGt(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_GT_FILTER_SERVICE.call(Type.fromTsDataType(type)).create(measurementIndex, value); } public static Filter gtEq(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueGtEq(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueGtEq(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueGtEq(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueGtEq( - measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueGtEq(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueGtEq(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueGtEq(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_GT_EQ_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, value); } public static Filter lt(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueLt(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueLt(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueLt(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueLt(measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueLt(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueLt(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueLt(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_LT_FILTER_SERVICE.call(Type.fromTsDataType(type)).create(measurementIndex, value); } public static Filter ltEq(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueLtEq(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueLtEq(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueLtEq(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueLtEq( - measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueLtEq(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueLtEq(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueLtEq(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_LT_EQ_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, value); } public static Filter eq(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueEq(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueEq(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueEq(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueEq(measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueEq(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueEq(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueEq(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_EQ_FILTER_SERVICE.call(Type.fromTsDataType(type)).create(measurementIndex, value); } public static Filter notEq(int measurementIndex, Object value, TSDataType type) { // constant cannot be null Objects.requireNonNull(value, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueNotEq(measurementIndex, (boolean) value); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueNotEq(measurementIndex, ((Number) value).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueNotEq(measurementIndex, ((Number) value).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueNotEq( - measurementIndex, ((Number) value).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueNotEq(measurementIndex, ((Number) value).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueNotEq(measurementIndex, (Binary) value); - case STRING: - return new StringFilterOperators.ValueNotEq(measurementIndex, (Binary) value); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_NOT_EQ_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, value); } public static Filter isNull(int measurementIndex) { @@ -231,35 +115,9 @@ public static Filter between( // constant cannot be null Objects.requireNonNull(value1, CONSTANT_CANNOT_BE_NULL_MSG); Objects.requireNonNull(value2, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueBetweenAnd( - measurementIndex, (boolean) value1, (boolean) value2); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueBetweenAnd( - measurementIndex, ((Number) value1).intValue(), ((Number) value2).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueBetweenAnd( - measurementIndex, ((Number) value1).longValue(), ((Number) value2).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueBetweenAnd( - measurementIndex, ((Number) value1).doubleValue(), ((Number) value2).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueBetweenAnd( - measurementIndex, ((Number) value1).floatValue(), ((Number) value2).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueBetweenAnd( - measurementIndex, (Binary) value1, (Binary) value2); - case STRING: - return new StringFilterOperators.ValueBetweenAnd( - measurementIndex, (Binary) value1, (Binary) value2); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_BETWEEN_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, value1, value2); } public static Filter notBetween( @@ -267,195 +125,53 @@ public static Filter notBetween( // constant cannot be null Objects.requireNonNull(value1, CONSTANT_CANNOT_BE_NULL_MSG); Objects.requireNonNull(value2, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueNotBetweenAnd( - measurementIndex, (boolean) value1, (boolean) value2); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueNotBetweenAnd( - measurementIndex, ((Number) value1).intValue(), ((Number) value2).intValue()); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueNotBetweenAnd( - measurementIndex, ((Number) value1).longValue(), ((Number) value2).longValue()); - case DOUBLE: - return new DoubleFilterOperators.ValueNotBetweenAnd( - measurementIndex, ((Number) value1).doubleValue(), ((Number) value2).doubleValue()); - case FLOAT: - return new FloatFilterOperators.ValueNotBetweenAnd( - measurementIndex, ((Number) value1).floatValue(), ((Number) value2).floatValue()); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueNotBetweenAnd( - measurementIndex, (Binary) value1, (Binary) value2); - case STRING: - return new StringFilterOperators.ValueNotBetweenAnd( - measurementIndex, (Binary) value1, (Binary) value2); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_NOT_BETWEEN_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, value1, value2); } public static Filter like(int measurementIndex, LikePattern pattern, TSDataType type) { Objects.requireNonNull(pattern, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueLike(measurementIndex, pattern); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueLike(measurementIndex, pattern); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueLike(measurementIndex, pattern); - case DOUBLE: - return new DoubleFilterOperators.ValueLike(measurementIndex, pattern); - case FLOAT: - return new FloatFilterOperators.ValueLike(measurementIndex, pattern); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueLike(measurementIndex, pattern); - case STRING: - return new StringFilterOperators.ValueLike(measurementIndex, pattern); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_LIKE_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, pattern); } public static Filter notLike(int measurementIndex, LikePattern pattern, TSDataType type) { Objects.requireNonNull(pattern, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueNotLike(measurementIndex, pattern); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueNotLike(measurementIndex, pattern); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueNotLike(measurementIndex, pattern); - case DOUBLE: - return new DoubleFilterOperators.ValueNotLike(measurementIndex, pattern); - case FLOAT: - return new FloatFilterOperators.ValueNotLike(measurementIndex, pattern); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueNotLike(measurementIndex, pattern); - case STRING: - return new StringFilterOperators.ValueNotLike(measurementIndex, pattern); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_NOT_LIKE_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, pattern); } public static Filter regexp(int measurementIndex, Pattern pattern, TSDataType type) { Objects.requireNonNull(pattern, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueRegexp(measurementIndex, pattern); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueRegexp(measurementIndex, pattern); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueRegexp(measurementIndex, pattern); - case DOUBLE: - return new DoubleFilterOperators.ValueRegexp(measurementIndex, pattern); - case FLOAT: - return new FloatFilterOperators.ValueRegexp(measurementIndex, pattern); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueRegexp(measurementIndex, pattern); - case STRING: - return new StringFilterOperators.ValueRegexp(measurementIndex, pattern); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_REGEXP_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, pattern); } public static Filter notRegexp(int measurementIndex, Pattern pattern, TSDataType type) { Objects.requireNonNull(pattern, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case DOUBLE: - return new DoubleFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case FLOAT: - return new FloatFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueNotRegexp(measurementIndex, pattern); - case STRING: - return new StringFilterOperators.ValueNotRegexp(measurementIndex, pattern); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_NOT_REGEXP_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, pattern); } public static > Filter in( int measurementIndex, Set values, TSDataType type) { // constants cannot be null Objects.requireNonNull(values, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueIn(measurementIndex, (Set) values); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueIn(measurementIndex, (Set) values); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueIn(measurementIndex, (Set) values); - case FLOAT: - return new FloatFilterOperators.ValueIn(measurementIndex, (Set) values); - case DOUBLE: - return new DoubleFilterOperators.ValueIn(measurementIndex, (Set) values); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueIn(measurementIndex, (Set) values); - case STRING: - return new StringFilterOperators.ValueIn(measurementIndex, (Set) values); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_IN_FILTER_SERVICE.call(Type.fromTsDataType(type)).create(measurementIndex, values); } public static > Filter notIn( int measurementIndex, Set values, TSDataType type) { // constants cannot be null Objects.requireNonNull(values, CONSTANT_CANNOT_BE_NULL_MSG); - switch (type) { - case BOOLEAN: - return new BooleanFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case INT32: - case DATE: - return new IntegerFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case INT64: - case TIMESTAMP: - return new LongFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case FLOAT: - return new FloatFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case DOUBLE: - return new DoubleFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case TEXT: - case BLOB: - return new BinaryFilterOperators.ValueNotIn(measurementIndex, (Set) values); - case STRING: - return new StringFilterOperators.ValueNotIn(measurementIndex, (Set) values); - default: - throw new UnsupportedOperationException( - Messages.format("error.read.filter_api_unsupported_type", type)); - } + return VALUE_NOT_IN_FILTER_SERVICE + .call(Type.fromTsDataType(type)) + .create(measurementIndex, values); } public static Filter extractValueGt( diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/DataSetWithoutTimeGenerator.java b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/DataSetWithoutTimeGenerator.java index 4e3b870ca..966131732 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/DataSetWithoutTimeGenerator.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/DataSetWithoutTimeGenerator.java @@ -20,13 +20,12 @@ package org.apache.tsfile.read.query.dataset; import org.apache.tsfile.enums.TSDataType; -import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.Field; import org.apache.tsfile.read.common.Path; import org.apache.tsfile.read.common.RowRecord; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.series.AbstractFileSeriesReader; -import org.apache.tsfile.write.UnSupportedDataTypeException; import java.io.IOException; import java.util.ArrayList; @@ -152,44 +151,10 @@ private Long timeHeapGet() { } private Field putValueToField(BatchData col) { - TSDataType type = col.getDataType(); - Field field; - if (type == TSDataType.VECTOR) { - field = new Field((col.getVector())[0].getDataType()); - } else { - field = new Field(col.getDataType()); - } - switch (col.getDataType()) { - case BOOLEAN: - field.setBoolV(col.getBoolean()); - break; - case INT32: - case DATE: - field.setIntV(col.getInt()); - break; - case INT64: - case TIMESTAMP: - field.setLongV(col.getLong()); - break; - case FLOAT: - field.setFloatV(col.getFloat()); - break; - case DOUBLE: - field.setDoubleV(col.getDouble()); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - field.setBinaryV(col.getBinary()); - break; - case VECTOR: - Field.setTsPrimitiveValue((col.getVector())[0], field); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.read.dataset_unsupported_type", col.getDataType())); - } + TSDataType dataType = col.getDataType(); + Field field = + new Field(dataType == TSDataType.VECTOR ? col.getVector()[0].getDataType() : dataType); + Type.fromTsDataType(dataType).setTo(col, field); return field; } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TableResultSet.java b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TableResultSet.java index dabe7533b..74f501429 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TableResultSet.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TableResultSet.java @@ -22,6 +22,7 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.block.TsBlockReader; import org.apache.tsfile.utils.DateUtils; import org.apache.tsfile.write.record.TSRecord; @@ -184,36 +185,7 @@ private boolean cacheNextRecord() throws IOException { cachedRecord.dataPointList.add(null); continue; } - switch (dataType) { - case INT32: - case DATE: - cachedRecord.addPoint(columnName, getInt(columnName)); - break; - case INT64: - case TIMESTAMP: - cachedRecord.addPoint(columnName, getLong(columnName)); - break; - case FLOAT: - cachedRecord.addPoint(columnName, getFloat(columnName)); - break; - case DOUBLE: - cachedRecord.addPoint(columnName, getDouble(columnName)); - break; - case STRING: - case TEXT: - cachedRecord.addPoint(columnName, getString(columnName)); - break; - case BLOB: - cachedRecord.addPoint(columnName, getBinary(columnName)); - break; - case BOOLEAN: - cachedRecord.addPoint(columnName, getBoolean(columnName)); - break; - case VECTOR: - case UNKNOWN: - default: - break; - } + Type.fromTsDataType(dataType).addPoint(cachedRecord, columnName, TableResultSet.this); } return true; } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TreeResultSet.java b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TreeResultSet.java index 9483e1e7e..6321e0622 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TreeResultSet.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/query/dataset/TreeResultSet.java @@ -23,6 +23,7 @@ import org.apache.tsfile.file.metadata.StringArrayDeviceID; import org.apache.tsfile.read.common.Field; import org.apache.tsfile.read.common.Path; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.write.record.TSRecord; import java.io.IOException; @@ -119,36 +120,7 @@ private boolean fetchRecords() throws IOException { Integer pathIdx = pathIndexMap.get(path); if (pathIdx != null) { Field field = currentRow.getField(pathIdx); - switch (field.getDataType()) { - case INT32: - case DATE: - record.addPoint(measurement, field.getIntV()); - break; - case INT64: - case TIMESTAMP: - record.addPoint(measurement, field.getLongV()); - break; - case FLOAT: - record.addPoint(measurement, field.getFloatV()); - break; - case DOUBLE: - record.addPoint(measurement, field.getDoubleV()); - break; - case STRING: - case TEXT: - record.addPoint(measurement, field.getStringValue()); - break; - case BLOB: - record.addPoint(measurement, field.getBinaryV().getValues()); - break; - case BOOLEAN: - record.addPoint(measurement, field.getBoolV()); - break; - case VECTOR: - case UNKNOWN: - default: - break; - } + Type.fromTsDataType(field.getDataType()).addPoint(record, measurement, field); } else { record.dataPointList.add(null); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/TsFileLastReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/TsFileLastReader.java index 83a454007..8700a850e 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/TsFileLastReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/TsFileLastReader.java @@ -32,6 +32,7 @@ import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.Chunk; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.reader.chunk.ChunkReader; import org.apache.tsfile.read.reader.page.ValuePageReader; import org.apache.tsfile.utils.Pair; @@ -231,11 +232,14 @@ private Pair convertToLastPoint( seriesMeta.getMeasurementId(), new TimeValuePair( seriesMeta.getStatistics().getEndTime(), - seriesMeta.getTsDataType() == TSDataType.VECTOR - ? TsPrimitiveType.getByType( - TSDataType.INT64, seriesMeta.getStatistics().getEndTime()) - : TsPrimitiveType.getByType( - seriesMeta.getTsDataType(), seriesMeta.getStatistics().getLastValue()))); + Type.fromTsDataType( + seriesMeta.getTsDataType() == TSDataType.VECTOR + ? TSDataType.INT64 + : seriesMeta.getTsDataType()) + .getTsPrimitiveType( + seriesMeta.getTsDataType() == TSDataType.VECTOR + ? seriesMeta.getStatistics().getEndTime() + : seriesMeta.getStatistics().getLastValue()))); } else { return readLastPoint(seriesMeta, isAligned); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/block/SingleDeviceTsBlockReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/block/SingleDeviceTsBlockReader.java index 8eaab414f..e149a648a 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/block/SingleDeviceTsBlockReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/block/SingleDeviceTsBlockReader.java @@ -25,6 +25,7 @@ import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.controller.IChunkLoader; import org.apache.tsfile.read.controller.IMetadataQuerier; import org.apache.tsfile.read.expression.ExpressionTree; @@ -32,16 +33,13 @@ import org.apache.tsfile.read.query.executor.task.DeviceQueryTask; import org.apache.tsfile.read.reader.series.AbstractFileSeriesReader; import org.apache.tsfile.read.reader.series.FileSeriesReader; -import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.TsPrimitiveType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -232,69 +230,12 @@ private void advanceColumn(BatchData batchData, MeasurementColumnContext columnC } private void fillIdColumn(Column column, Object val, int startPos, int endPos) { - switch (column.getDataType()) { - case TEXT: - case STRING: - case BLOB: - case OBJECT: - if (val instanceof String) { - val = new Binary(((String) val), StandardCharsets.UTF_8); - } - Arrays.fill(column.getBinaries(), startPos, endPos, val); - break; - case BOOLEAN: - Arrays.fill(column.getBooleans(), startPos, endPos, ((boolean) val)); - break; - case INT32: - case DATE: - Arrays.fill(column.getInts(), startPos, endPos, ((int) val)); - break; - case INT64: - case TIMESTAMP: - Arrays.fill(column.getLongs(), startPos, endPos, ((long) val)); - break; - case FLOAT: - Arrays.fill(column.getFloats(), startPos, endPos, ((float) val)); - break; - case DOUBLE: - Arrays.fill(column.getDoubles(), startPos, endPos, ((double) val)); - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.block_reader_unsupported_type", column.getDataType())); - } + Type.fromTsDataType(column.getDataType()).setTo(val, column, startPos, endPos); column.setPositionCount(endPos); } private static void fillSingleMeasurementColumn(Column column, BatchData batchData, int pos) { - switch (batchData.getDataType()) { - case BOOLEAN: - column.getBooleans()[pos] = batchData.getBoolean(); - break; - case DOUBLE: - column.getDoubles()[pos] = batchData.getDouble(); - break; - case FLOAT: - column.getFloats()[pos] = batchData.getFloat(); - break; - case INT32: - case DATE: - column.getInts()[pos] = batchData.getInt(); - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - column.getBinaries()[pos] = batchData.getBinary(); - break; - case INT64: - case TIMESTAMP: - column.getLongs()[pos] = batchData.getLong(); - break; - default: - throw new IllegalArgumentException( - Messages.format("error.read.block_reader_unsupported_type", batchData.getDataType())); - } + Type.fromTsDataType(batchData.getDataType()).setTo(batchData, column, pos); column.setPositionCount(pos + 1); } @@ -383,35 +324,8 @@ void fillInto(TsBlock block, int blockRowNum) { final List columnPositions = posInResult.get(i); for (Integer pos : columnPositions) { if (value != null) { - switch (value.getDataType()) { - case TEXT: - case STRING: - case BLOB: - case OBJECT: - block.getColumn(pos).getBinaries()[blockRowNum] = value.getBinary(); - break; - case INT32: - case DATE: - block.getColumn(pos).getInts()[blockRowNum] = value.getInt(); - break; - case INT64: - case TIMESTAMP: - block.getColumn(pos).getLongs()[blockRowNum] = value.getLong(); - break; - case BOOLEAN: - block.getColumn(pos).getBooleans()[blockRowNum] = value.getBoolean(); - break; - case FLOAT: - block.getColumn(pos).getFloats()[blockRowNum] = value.getFloat(); - break; - case DOUBLE: - block.getColumn(pos).getDoubles()[blockRowNum] = value.getDouble(); - break; - default: - throw new IllegalArgumentException( - Messages.format( - "error.read.block_reader_unsupported_type", value.getDataType())); - } + Type.fromTsDataType(value.getDataType()) + .setTo(value, block.getColumn(pos), blockRowNum); } else { block.getColumn(pos).setNull(blockRowNum, blockRowNum + 1); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/PageReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/PageReader.java index 2db020cf8..60a877a41 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/PageReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/PageReader.java @@ -19,7 +19,6 @@ package org.apache.tsfile.read.reader.page; -import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.encoding.decoder.Decoder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.header.PageHeader; @@ -29,14 +28,16 @@ import org.apache.tsfile.read.common.TimeRange; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; -import org.apache.tsfile.read.common.block.column.TimeColumnBuilder; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.read.filter.factory.FilterFactory; import org.apache.tsfile.read.reader.IPageReader; import org.apache.tsfile.read.reader.series.PaginationController; -import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.ReadWriteForEncodingUtils; -import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.utils.TypeServices; +import org.apache.tsfile.utils.TypeServices.PageDataBlockValueReader; +import org.apache.tsfile.utils.TypeServices.PageDataReadStatus; +import org.apache.tsfile.utils.TypeServices.PageDataValueReader; import java.io.IOException; import java.io.Serializable; @@ -45,6 +46,7 @@ import java.util.List; import java.util.Optional; import java.util.function.LongConsumer; +import java.util.function.LongPredicate; import static org.apache.tsfile.read.reader.series.PaginationController.UNLIMITED_PAGINATION_CONTROLLER; import static org.apache.tsfile.utils.Preconditions.checkArgument; @@ -149,64 +151,18 @@ private void uncompressDataIfNecessary() throws IOException { /** * @return the returned BatchData may be empty, but never be null */ - @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning @Override public BatchData getAllSatisfiedPageData(boolean ascending) throws IOException { uncompressDataIfNecessary(); BatchData pageData = BatchDataFactory.createBatchData(dataType, ascending, false); boolean allSatisfy = recordFilter == null || recordFilter.allSatisfy(this); + PageDataValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_BATCHDATA_SERVICE.call(Type.fromTsDataType(dataType)); + LongPredicate isDeleted = this::isDeleted; while (timeDecoder.hasNext(timeBuffer)) { long timestamp = timeDecoder.readLong(timeBuffer); - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (!isDeleted(timestamp) - && (allSatisfy || recordFilter.satisfyBoolean(timestamp, aBoolean))) { - pageData.putBoolean(timestamp, aBoolean); - } - break; - case INT32: - case DATE: - int anInt = valueDecoder.readInt(valueBuffer); - if (!isDeleted(timestamp) - && (allSatisfy || recordFilter.satisfyInteger(timestamp, anInt))) { - pageData.putInt(timestamp, anInt); - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (!isDeleted(timestamp) && (allSatisfy || recordFilter.satisfyLong(timestamp, aLong))) { - pageData.putLong(timestamp, aLong); - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (!isDeleted(timestamp) - && (allSatisfy || recordFilter.satisfyFloat(timestamp, aFloat))) { - pageData.putFloat(timestamp, aFloat); - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (!isDeleted(timestamp) - && (allSatisfy || recordFilter.satisfyDouble(timestamp, aDouble))) { - pageData.putDouble(timestamp, aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (!isDeleted(timestamp) - && (allSatisfy || recordFilter.satisfyBinary(timestamp, aBinary))) { - pageData.putBinary(timestamp, aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + valueReader.read( + valueDecoder, valueBuffer, recordFilter, pageData, timestamp, allSatisfy, isDeleted); } return pageData.flip(); } @@ -220,175 +176,35 @@ public TsBlock getAllSatisfiedData() throws IOException { public TsBlock getAllSatisfiedData(LongConsumer filterRowsRecorder) throws IOException { uncompressDataIfNecessary(); TsBlockBuilder builder; - int initialExpectedEntries = (int) pageHeader.getStatistics().getCount(); + int initialExpectedEntries = pageHeader.getStatistics().getCount(); if (paginationController.hasSetLimit()) { initialExpectedEntries = (int) Math.min(initialExpectedEntries, paginationController.getCurLimit()); } builder = new TsBlockBuilder(initialExpectedEntries, Collections.singletonList(dataType)); - TimeColumnBuilder timeBuilder = builder.getTimeColumnBuilder(); - ColumnBuilder valueBuilder = builder.getColumnBuilder(0); long allFilteredRows = 0; boolean allSatisfy = recordFilter == null || recordFilter.allSatisfy(this); - switch (dataType) { - case BOOLEAN: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyBoolean(timestamp, aBoolean)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeBoolean(aBoolean); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } - break; - case INT32: - case DATE: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - int anInt = valueDecoder.readInt(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyInteger(timestamp, anInt)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeInt(anInt); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } - break; - case INT64: - case TIMESTAMP: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - long aLong = valueDecoder.readLong(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyLong(timestamp, aLong)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeLong(aLong); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } - break; - case FLOAT: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - float aFloat = valueDecoder.readFloat(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyFloat(timestamp, aFloat)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeFloat(aFloat); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } - break; - case DOUBLE: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - double aDouble = valueDecoder.readDouble(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyDouble(timestamp, aDouble)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeDouble(aDouble); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - while (timeDecoder.hasNext(timeBuffer)) { - long timestamp = timeDecoder.readLong(timeBuffer); - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (isDeleted(timestamp)) { - continue; - } - if (!allSatisfy && !recordFilter.satisfyBinary(timestamp, aBinary)) { - allFilteredRows++; - continue; - } - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - timeBuilder.writeLong(timestamp); - valueBuilder.writeBinary(aBinary); - builder.declarePosition(); - paginationController.consumeLimit(); - } else { - break; - } - } + PageDataBlockValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_TSBLOCK_SERVICE.call(Type.fromTsDataType(dataType)); + LongPredicate isDeleted = this::isDeleted; + while (timeDecoder.hasNext(timeBuffer)) { + long timestamp = timeDecoder.readLong(timeBuffer); + PageDataReadStatus status = + valueReader.read( + valueDecoder, + valueBuffer, + recordFilter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController); + if (status == PageDataReadStatus.FILTERED) { + allFilteredRows++; + } else if (status == PageDataReadStatus.STOP) { break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); + } } if (filterRowsRecorder != null && allFilteredRows > 0) { filterRowsRecorder.accept(allFilteredRows); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/ValuePageReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/ValuePageReader.java index 50835f5f8..cd0799f3f 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/ValuePageReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/ValuePageReader.java @@ -27,18 +27,21 @@ import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.BatchDataFactory; import org.apache.tsfile.read.common.TimeRange; -import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.filter.basic.Filter; -import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.apache.tsfile.utils.TsPrimitiveType; -import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.utils.TypeServices; +import org.apache.tsfile.utils.TypeServices.PageDataColumnBuilderValueReader; +import org.apache.tsfile.utils.TypeServices.PageDataTsPrimitiveValueReader; +import org.apache.tsfile.utils.TypeServices.PageDataValueReader; import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; +import java.util.function.LongPredicate; public class ValuePageReader { @@ -115,120 +118,29 @@ public BatchData nextBatch(long[] timeBatch, boolean ascending, Filter filter) throws IOException { uncompressDataIfNecessary(); BatchData pageData = BatchDataFactory.createBatchData(dataType, ascending, false); + PageDataValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_BATCHDATA_SERVICE.call(Type.fromTsDataType(dataType)); + boolean allSatisfy = filter == null; + LongPredicate isDeleted = this::isDeleted; for (int i = 0; i < timeBatch.length; i++) { if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { continue; } long timestamp = timeBatch[i]; - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (!isDeleted(timestamp) - && (filter == null || filter.satisfyBoolean(timestamp, aBoolean))) { - pageData.putBoolean(timestamp, aBoolean); - } - break; - case INT32: - case DATE: - int anInt = valueDecoder.readInt(valueBuffer); - if (!isDeleted(timestamp) - && (filter == null || filter.satisfyInteger(timestamp, anInt))) { - pageData.putInt(timestamp, anInt); - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (!isDeleted(timestamp) && (filter == null || filter.satisfyLong(timestamp, aLong))) { - pageData.putLong(timestamp, aLong); - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (!isDeleted(timestamp) && (filter == null || filter.satisfyFloat(timestamp, aFloat))) { - pageData.putFloat(timestamp, aFloat); - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (!isDeleted(timestamp) - && (filter == null || filter.satisfyDouble(timestamp, aDouble))) { - pageData.putDouble(timestamp, aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (!isDeleted(timestamp) - && (filter == null || filter.satisfyBinary(timestamp, aBinary))) { - pageData.putBinary(timestamp, aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + valueReader.read( + valueDecoder, valueBuffer, filter, pageData, timestamp, allSatisfy, isDeleted); } return pageData.flip(); } public TsPrimitiveType nextValue(long timestamp, int timeIndex) throws IOException { uncompressDataIfNecessary(); - TsPrimitiveType resultValue = null; if (valueBuffer == null || ((bitmap[timeIndex / 8] & 0xFF) & (MASK >>> (timeIndex % 8))) == 0) { return null; } - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = new TsPrimitiveType.TsBoolean(aBoolean); - } - break; - case INT32: - case DATE: - int anInt = valueDecoder.readInt(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = - dataType.equals(TSDataType.INT32) - ? new TsPrimitiveType.TsInt(anInt) - : new TsPrimitiveType.TsInt(anInt, TSDataType.DATE); - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = new TsPrimitiveType.TsLong(aLong); - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = new TsPrimitiveType.TsFloat(aFloat); - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = new TsPrimitiveType.TsDouble(aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (!isDeleted(timestamp)) { - resultValue = new TsPrimitiveType.TsBinary(aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } - - return resultValue; + PageDataTsPrimitiveValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_TSPRIMITIVETYPE_SERVICE.call(Type.fromTsDataType(dataType)); + return valueReader.read(valueDecoder, valueBuffer, timestamp, this::isDeleted); } /** @@ -241,58 +153,14 @@ public TsPrimitiveType[] nextValueBatch(long[] timeBatch) throws IOException { if (valueBuffer == null) { return valueBatch; } + PageDataTsPrimitiveValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_TSPRIMITIVETYPE_SERVICE.call(Type.fromTsDataType(dataType)); + LongPredicate isDeleted = this::isDeleted; for (int i = 0; i < size; i++) { if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { continue; } - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = new TsPrimitiveType.TsBoolean(aBoolean); - } - break; - case INT32: - case DATE: - int anInt = valueDecoder.readInt(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = - dataType.equals(TSDataType.INT32) - ? new TsPrimitiveType.TsInt(anInt) - : new TsPrimitiveType.TsInt(anInt, TSDataType.DATE); - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = new TsPrimitiveType.TsLong(aLong); - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = new TsPrimitiveType.TsFloat(aFloat); - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = new TsPrimitiveType.TsDouble(aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (!isDeleted(timeBatch[i])) { - valueBatch[i] = new TsPrimitiveType.TsBinary(aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + valueBatch[i] = valueReader.read(valueDecoder, valueBuffer, timeBatch[i], isDeleted); } return valueBatch; } @@ -309,6 +177,8 @@ public void writeColumnBuilderWithNextBatch( } return; } + PageDataColumnBuilderValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_COLUMNBUILDER_SERVICE.call(Type.fromTsDataType(dataType)); for (int i = 0; i < readEndIndex; i++) { if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { if (keepCurrentRow[i]) { @@ -316,88 +186,7 @@ public void writeColumnBuilderWithNextBatch( } continue; } - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeBoolean(aBoolean); - } - } - break; - case INT32: - int anInt = valueDecoder.readInt(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeInt(anInt); - } - } - break; - case DATE: - int anDate = valueDecoder.readInt(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - if (columnBuilder instanceof BinaryColumnBuilder) { - ((BinaryColumnBuilder) columnBuilder).writeDate(anDate); - } else { - columnBuilder.writeInt(anDate); - } - } - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeLong(aLong); - } - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeFloat(aFloat); - } - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeDouble(aDouble); - } - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (keepCurrentRow[i]) { - if (isDeleted[i]) { - columnBuilder.appendNull(); - } else { - columnBuilder.writeBinary(aBinary); - } - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + valueReader.read(valueDecoder, valueBuffer, columnBuilder, keepCurrentRow[i], isDeleted[i]); } } @@ -412,6 +201,8 @@ public void writeColumnBuilderWithNextBatch( } return; } + PageDataColumnBuilderValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_COLUMNBUILDER_SERVICE.call(Type.fromTsDataType(dataType)); for (int i = 0; i < readEndIndex; i++) { if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { if (keepCurrentRow[i]) { @@ -419,60 +210,7 @@ public void writeColumnBuilderWithNextBatch( } continue; } - switch (dataType) { - case BOOLEAN: - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeBoolean(aBoolean); - } - break; - case INT32: - int anInt = valueDecoder.readInt(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeInt(anInt); - } - break; - case DATE: - int anDate = valueDecoder.readInt(valueBuffer); - if (keepCurrentRow[i]) { - if (columnBuilder instanceof BinaryColumnBuilder) { - ((BinaryColumnBuilder) columnBuilder).writeDate(anDate); - } else { - columnBuilder.writeInt(anDate); - } - } - break; - case INT64: - case TIMESTAMP: - long aLong = valueDecoder.readLong(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeLong(aLong); - } - break; - case FLOAT: - float aFloat = valueDecoder.readFloat(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeFloat(aFloat); - } - break; - case DOUBLE: - double aDouble = valueDecoder.readDouble(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeDouble(aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - Binary aBinary = valueDecoder.readBinary(valueBuffer); - if (keepCurrentRow[i]) { - columnBuilder.writeBinary(aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); - } + valueReader.read(valueDecoder, valueBuffer, columnBuilder, keepCurrentRow[i], false); } } @@ -483,131 +221,20 @@ public void writeColumnBuilderWithNextBatch( columnBuilder.appendNull(readEndIndex - readStartIndex); return; } - - switch (dataType) { - case BOOLEAN: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readBoolean(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - boolean aBoolean = valueDecoder.readBoolean(valueBuffer); - columnBuilder.writeBoolean(aBoolean); - } - break; - case INT32: - case DATE: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readInt(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - int aInt = valueDecoder.readInt(valueBuffer); - if (dataType == TSDataType.INT32) { - columnBuilder.writeInt(aInt); - } else { - if (columnBuilder instanceof BinaryColumnBuilder) { - ((BinaryColumnBuilder) columnBuilder).writeDate(aInt); - } else { - columnBuilder.writeInt(aInt); - } - } - } - break; - case INT64: - case TIMESTAMP: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readLong(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - long aLong = valueDecoder.readLong(valueBuffer); - columnBuilder.writeLong(aLong); - } - break; - case FLOAT: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readFloat(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - float aFloat = valueDecoder.readFloat(valueBuffer); - columnBuilder.writeFloat(aFloat); - } - break; - case DOUBLE: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readDouble(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - double aDouble = valueDecoder.readDouble(valueBuffer); - columnBuilder.writeDouble(aDouble); - } - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - // skip useless data - for (int i = 0; i < readStartIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - continue; - } - valueDecoder.readBinary(valueBuffer); - } - - for (int i = readStartIndex; i < readEndIndex; i++) { - if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { - columnBuilder.appendNull(); - continue; - } - Binary aBinary = valueDecoder.readBinary(valueBuffer); - columnBuilder.writeBinary(aBinary); - } - break; - default: - throw new UnSupportedDataTypeException(String.valueOf(dataType)); + PageDataColumnBuilderValueReader valueReader = + TypeServices.READ_PAGE_VALUE_TO_COLUMNBUILDER_SERVICE.call(Type.fromTsDataType(dataType)); + // skip useless data + for (int i = 0; i < readStartIndex; i++) { + if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) != 0) { + valueReader.read(valueDecoder, valueBuffer, columnBuilder, false, false); + } + } + for (int i = readStartIndex; i < readEndIndex; i++) { + if (((bitmap[i / 8] & 0xFF) & (MASK >>> (i % 8))) == 0) { + columnBuilder.appendNull(); + continue; + } + valueReader.read(valueDecoder, valueBuffer, columnBuilder, true, false); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/utils/EncodingTypeServices.java b/java/tsfile/src/main/java/org/apache/tsfile/utils/EncodingTypeServices.java new file mode 100644 index 000000000..145f5abd0 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/utils/EncodingTypeServices.java @@ -0,0 +1,198 @@ +/* + * 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.tsfile.utils; + +import org.apache.tsfile.encoding.encoder.DeltaBinaryEncoder; +import org.apache.tsfile.encoding.encoder.DoublePrecisionChimpEncoder; +import org.apache.tsfile.encoding.encoder.DoublePrecisionEncoderV1; +import org.apache.tsfile.encoding.encoder.DoublePrecisionEncoderV2; +import org.apache.tsfile.encoding.encoder.DoubleRLBE; +import org.apache.tsfile.encoding.encoder.DoubleSprintzEncoder; +import org.apache.tsfile.encoding.encoder.Encoder; +import org.apache.tsfile.encoding.encoder.FloatEncoder; +import org.apache.tsfile.encoding.encoder.FloatRLBE; +import org.apache.tsfile.encoding.encoder.FloatSprintzEncoder; +import org.apache.tsfile.encoding.encoder.IntChimpEncoder; +import org.apache.tsfile.encoding.encoder.IntGorillaEncoder; +import org.apache.tsfile.encoding.encoder.IntRLBE; +import org.apache.tsfile.encoding.encoder.IntRleEncoder; +import org.apache.tsfile.encoding.encoder.IntSprintzEncoder; +import org.apache.tsfile.encoding.encoder.IntZigzagEncoder; +import org.apache.tsfile.encoding.encoder.LongChimpEncoder; +import org.apache.tsfile.encoding.encoder.LongGorillaEncoder; +import org.apache.tsfile.encoding.encoder.LongRLBE; +import org.apache.tsfile.encoding.encoder.LongRleEncoder; +import org.apache.tsfile.encoding.encoder.LongSprintzEncoder; +import org.apache.tsfile.encoding.encoder.LongZigzagEncoder; +import org.apache.tsfile.encoding.encoder.RegularDataEncoder; +import org.apache.tsfile.encoding.encoder.SinglePrecisionChimpEncoder; +import org.apache.tsfile.encoding.encoder.SinglePrecisionEncoderV1; +import org.apache.tsfile.encoding.encoder.SinglePrecisionEncoderV2; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.service.TypeService; +import org.apache.tsfile.write.UnSupportedDataTypeException; + +public final class EncodingTypeServices { + + private static final String ERROR_MSG_KEY = "error.encoding.ts_encoding_builder_unsupported_type"; + + public static final TypeService RLE_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE, BOOLEAN -> maxPointNumber -> new IntRleEncoder(); + case INT64, TIMESTAMP -> maxPointNumber -> new LongRleEncoder(); + case FLOAT -> + maxPointNumber -> + new FloatEncoder(TSEncoding.RLE, TSDataType.FLOAT, maxPointNumber); + case DOUBLE -> + maxPointNumber -> + new FloatEncoder(TSEncoding.RLE, TSDataType.DOUBLE, maxPointNumber); + case TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + maxPointNumber -> { + throw unsupported(TSEncoding.RLE, type.getTypeEnum()); + }; + }; + + public static final TypeService TS_2DIFF_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> maxPointNumber -> new DeltaBinaryEncoder.IntDeltaEncoder(); + case INT64, TIMESTAMP -> maxPointNumber -> new DeltaBinaryEncoder.LongDeltaEncoder(); + case FLOAT -> + maxPointNumber -> + new FloatEncoder(TSEncoding.TS_2DIFF, TSDataType.FLOAT, maxPointNumber); + case DOUBLE -> + maxPointNumber -> + new FloatEncoder(TSEncoding.TS_2DIFF, TSDataType.DOUBLE, maxPointNumber); + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + maxPointNumber -> { + throw unsupported(TSEncoding.TS_2DIFF, type.getTypeEnum()); + }; + }; + + public static final TypeService GORILLA_V1_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case FLOAT -> new SinglePrecisionEncoderV1(); + case DOUBLE -> new DoublePrecisionEncoderV1(); + case BOOLEAN, + INT32, + DATE, + INT64, + TIMESTAMP, + TEXT, + BLOB, + STRING, + OBJECT, + ROW, + UNKNOWN, + VECTOR -> + throw unsupported(TSEncoding.GORILLA_V1, type.getTypeEnum()); + }; + + public static final TypeService REGULAR_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> new RegularDataEncoder.IntRegularEncoder(); + case INT64, TIMESTAMP -> new RegularDataEncoder.LongRegularEncoder(); + case BOOLEAN, FLOAT, DOUBLE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.REGULAR, type.getTypeEnum()); + }; + + public static final TypeService GORILLA_V2_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case FLOAT -> new SinglePrecisionEncoderV2(); + case DOUBLE -> new DoublePrecisionEncoderV2(); + case INT32, DATE -> new IntGorillaEncoder(); + case INT64, TIMESTAMP -> new LongGorillaEncoder(); + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.GORILLA, type.getTypeEnum()); + }; + + public static final TypeService SPRINTZ_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> new IntSprintzEncoder(); + case INT64, TIMESTAMP -> new LongSprintzEncoder(); + case FLOAT -> new FloatSprintzEncoder(); + case DOUBLE -> new DoubleSprintzEncoder(); + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.SPRINTZ, type.getTypeEnum()); + }; + + public static final TypeService RLBE_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> new IntRLBE(); + case INT64, TIMESTAMP -> new LongRLBE(); + case FLOAT -> new FloatRLBE(); + case DOUBLE -> new DoubleRLBE(); + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.RLBE, type.getTypeEnum()); + }; + + public static final TypeService ZIGZAG_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> new IntZigzagEncoder(); + case INT64, TIMESTAMP -> new LongZigzagEncoder(); + case BOOLEAN, FLOAT, DOUBLE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.ZIGZAG, type.getTypeEnum()); + }; + + public static final TypeService CHIMP_ENCODER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case FLOAT -> new SinglePrecisionChimpEncoder(); + case DOUBLE -> new DoublePrecisionChimpEncoder(); + case INT32, DATE -> new IntChimpEncoder(); + case INT64, TIMESTAMP -> new LongChimpEncoder(); + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + throw unsupported(TSEncoding.CHIMP, type.getTypeEnum()); + }; + + static { + RLE_ENCODER_SERVICE.check(); + TS_2DIFF_ENCODER_SERVICE.check(); + GORILLA_V1_ENCODER_SERVICE.check(); + REGULAR_ENCODER_SERVICE.check(); + GORILLA_V2_ENCODER_SERVICE.check(); + SPRINTZ_ENCODER_SERVICE.check(); + RLBE_ENCODER_SERVICE.check(); + ZIGZAG_ENCODER_SERVICE.check(); + CHIMP_ENCODER_SERVICE.check(); + } + + private EncodingTypeServices() {} + + private static UnSupportedDataTypeException unsupported(TSEncoding encoding, Object type) { + return new UnSupportedDataTypeException(Messages.format(ERROR_MSG_KEY, encoding, type)) + .setChecked(true); + } + + @FunctionalInterface + public interface EncoderBuilder { + + Encoder build(int maxPointNumber); + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileGeneratorUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileGeneratorUtils.java index 6b941e73f..6d1dd9e15 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileGeneratorUtils.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileGeneratorUtils.java @@ -27,15 +27,11 @@ import org.apache.tsfile.fileSystem.fsFactory.FSFactory; import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.Path; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.write.TsFileWriter; import org.apache.tsfile.write.record.TSRecord; import org.apache.tsfile.write.record.Tablet; -import org.apache.tsfile.write.record.datapoint.BooleanDataPoint; import org.apache.tsfile.write.record.datapoint.DataPoint; -import org.apache.tsfile.write.record.datapoint.DoubleDataPoint; -import org.apache.tsfile.write.record.datapoint.FloatDataPoint; -import org.apache.tsfile.write.record.datapoint.IntDataPoint; -import org.apache.tsfile.write.record.datapoint.LongDataPoint; import org.apache.tsfile.write.record.datapoint.StringDataPoint; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.apache.tsfile.write.schema.MeasurementSchema; @@ -68,35 +64,9 @@ public static void writeWithTsRecord( // construct TsRecord TSRecord tsRecord = new TSRecord(deviceId, time); for (IMeasurementSchema schema : schemas) { - DataPoint dPoint; - switch (schema.getType()) { - case INT64: - case TIMESTAMP: - dPoint = new LongDataPoint(schema.getMeasurementName(), startValue); - break; - case INT32: - case DATE: - dPoint = new IntDataPoint(schema.getMeasurementName(), (int) startValue); - break; - case DOUBLE: - dPoint = new DoubleDataPoint(schema.getMeasurementName(), (double) startValue); - break; - case FLOAT: - dPoint = new FloatDataPoint(schema.getMeasurementName(), (float) startValue); - break; - case BOOLEAN: - dPoint = new BooleanDataPoint(schema.getMeasurementName(), true); - break; - case TEXT: - case BLOB: - case STRING: - default: - dPoint = - new StringDataPoint( - schema.getMeasurementName(), - new Binary(String.valueOf(startValue), TSFileConfig.STRING_CHARSET)); - break; - } + DataPoint dPoint = + Type.fromTsDataType(schema.getType()) + .getDataPoint(schema.getMeasurementName(), startValue); tsRecord.addTuple(dPoint); } // write diff --git a/java/tsfile/src/main/java/org/apache/tsfile/utils/TypeServices.java b/java/tsfile/src/main/java/org/apache/tsfile/utils/TypeServices.java new file mode 100644 index 000000000..34c5c16d6 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/utils/TypeServices.java @@ -0,0 +1,510 @@ +/* + * 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.tsfile.utils; + +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.encoding.decoder.Decoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.tsfile.read.common.type.service.TypeService; +import org.apache.tsfile.read.filter.basic.Filter; +import org.apache.tsfile.read.reader.series.PaginationController; +import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.write.chunk.ValueChunkWriter; + +import java.nio.ByteBuffer; +import java.util.function.LongPredicate; + +public final class TypeServices { + + // Page value decoding services for BatchData and TsBlock outputs. + + public static final TypeService READ_PAGE_VALUE_TO_BATCHDATA_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + boolean value = decoder.readBoolean(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyBoolean(timestamp, value))) { + pageData.putBoolean(timestamp, value); + } + }; + case INT32, DATE -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + int value = decoder.readInt(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyInteger(timestamp, value))) { + pageData.putInt(timestamp, value); + } + }; + case INT64, TIMESTAMP -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + long value = decoder.readLong(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyLong(timestamp, value))) { + pageData.putLong(timestamp, value); + } + }; + case FLOAT -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + float value = decoder.readFloat(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyFloat(timestamp, value))) { + pageData.putFloat(timestamp, value); + } + }; + case DOUBLE -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + double value = decoder.readDouble(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyDouble(timestamp, value))) { + pageData.putDouble(timestamp, value); + } + }; + case TEXT, BLOB, STRING, OBJECT -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + Binary value = decoder.readBinary(buffer); + if (!isDeleted.test(timestamp) + && (allSatisfy || filter.satisfyBinary(timestamp, value))) { + pageData.putBinary(timestamp, value); + } + }; + case ROW, UNKNOWN, VECTOR -> + (decoder, buffer, filter, pageData, timestamp, allSatisfy, isDeleted) -> { + throw new UnSupportedDataTypeException(String.valueOf(type.getTypeEnum())) + .setChecked(true); + }; + }; + + public static final TypeService READ_PAGE_VALUE_TO_TSBLOCK_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + boolean value = decoder.readBoolean(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyBoolean(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeBoolean(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case INT32, DATE -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + int value = decoder.readInt(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyInteger(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeInt(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case INT64, TIMESTAMP -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + long value = decoder.readLong(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyLong(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeLong(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case FLOAT -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + float value = decoder.readFloat(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyFloat(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeFloat(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case DOUBLE -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + double value = decoder.readDouble(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyDouble(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeDouble(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case TEXT, BLOB, STRING, OBJECT -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + Binary value = decoder.readBinary(buffer); + if (isDeleted.test(timestamp)) { + return PageDataReadStatus.CONTINUE; + } + if (!allSatisfy && !filter.satisfyBinary(timestamp, value)) { + return PageDataReadStatus.FILTERED; + } + if (paginationController.hasCurOffset()) { + paginationController.consumeOffset(); + return PageDataReadStatus.CONTINUE; + } + if (!paginationController.hasCurLimit()) { + return PageDataReadStatus.STOP; + } + builder.getTimeColumnBuilder().writeLong(timestamp); + builder.getColumnBuilder(0).writeBinary(value); + builder.declarePosition(); + paginationController.consumeLimit(); + return PageDataReadStatus.CONTINUE; + }; + case ROW, UNKNOWN, VECTOR -> + (decoder, + buffer, + filter, + builder, + timestamp, + allSatisfy, + isDeleted, + paginationController) -> { + throw new UnSupportedDataTypeException(String.valueOf(type.getTypeEnum())) + .setChecked(true); + }; + }; + + // Value page decoding services for TsPrimitiveType and ColumnBuilder outputs. + + public static final TypeService + READ_PAGE_VALUE_TO_TSPRIMITIVETYPE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (decoder, buffer, timestamp, isDeleted) -> { + boolean value = decoder.readBoolean(buffer); + return isDeleted.test(timestamp) + ? null + : new TsPrimitiveType.TsBoolean(value); + }; + case INT32 -> + (decoder, buffer, timestamp, isDeleted) -> { + int value = decoder.readInt(buffer); + return isDeleted.test(timestamp) ? null : new TsPrimitiveType.TsInt(value); + }; + case DATE -> + (decoder, buffer, timestamp, isDeleted) -> { + int value = decoder.readInt(buffer); + return isDeleted.test(timestamp) + ? null + : new TsPrimitiveType.TsInt(value, TSDataType.DATE); + }; + case INT64, TIMESTAMP -> + (decoder, buffer, timestamp, isDeleted) -> { + long value = decoder.readLong(buffer); + return isDeleted.test(timestamp) ? null : new TsPrimitiveType.TsLong(value); + }; + case FLOAT -> + (decoder, buffer, timestamp, isDeleted) -> { + float value = decoder.readFloat(buffer); + return isDeleted.test(timestamp) ? null : new TsPrimitiveType.TsFloat(value); + }; + case DOUBLE -> + (decoder, buffer, timestamp, isDeleted) -> { + double value = decoder.readDouble(buffer); + return isDeleted.test(timestamp) ? null : new TsPrimitiveType.TsDouble(value); + }; + case TEXT, BLOB, STRING, OBJECT -> + (decoder, buffer, timestamp, isDeleted) -> { + Binary value = decoder.readBinary(buffer); + return isDeleted.test(timestamp) ? null : new TsPrimitiveType.TsBinary(value); + }; + case ROW, UNKNOWN, VECTOR -> + (decoder, buffer, timestamp, isDeleted) -> { + throw new UnSupportedDataTypeException(String.valueOf(type.getTypeEnum())) + .setChecked(true); + }; + }; + + public static final TypeService + READ_PAGE_VALUE_TO_COLUMNBUILDER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + boolean value = decoder.readBoolean(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeBoolean(value); + } + } + }; + case INT32 -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + int value = decoder.readInt(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeInt(value); + } + } + }; + case DATE -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + int value = decoder.readInt(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else if (builder instanceof BinaryColumnBuilder) { + ((BinaryColumnBuilder) builder).writeDate(value); + } else { + builder.writeInt(value); + } + } + }; + case INT64, TIMESTAMP -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + long value = decoder.readLong(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeLong(value); + } + } + }; + case FLOAT -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + float value = decoder.readFloat(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeFloat(value); + } + } + }; + case DOUBLE -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + double value = decoder.readDouble(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeDouble(value); + } + } + }; + case TEXT, BLOB, STRING, OBJECT -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + Binary value = decoder.readBinary(buffer); + if (keepCurrentRow) { + if (isDeleted) { + builder.appendNull(); + } else { + builder.writeBinary(value); + } + } + }; + case ROW, UNKNOWN, VECTOR -> + (decoder, buffer, builder, keepCurrentRow, isDeleted) -> { + throw new UnSupportedDataTypeException(String.valueOf(type.getTypeEnum())) + .setChecked(true); + }; + }; + + // Chunk writing services. + + public static final TypeService WRITE_EMPTY_VALUE_TO_CHUNK_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> writer -> writer.write(-1, false, true); + case INT32, DATE -> writer -> writer.write(-1, 0, true); + case INT64, TIMESTAMP -> writer -> writer.write(-1, 0L, true); + case FLOAT -> writer -> writer.write(-1, 0.0F, true); + case DOUBLE -> writer -> writer.write(-1, 0.0D, true); + case TEXT, BLOB, STRING, OBJECT -> writer -> writer.write(-1, null, true); + case ROW, UNKNOWN, VECTOR -> + writer -> { + throw new UnSupportedDataTypeException( + Messages.format("error.write.type_not_supported", type.getTypeEnum())) + .setChecked(true); + }; + }; + + private TypeServices() {} + + @FunctionalInterface + public interface PageDataValueReader { + + void read( + Decoder decoder, + ByteBuffer buffer, + Filter filter, + BatchData pageData, + long timestamp, + boolean allSatisfy, + LongPredicate isDeleted); + } + + @FunctionalInterface + public interface PageDataBlockValueReader { + + PageDataReadStatus read( + Decoder decoder, + ByteBuffer buffer, + Filter filter, + TsBlockBuilder builder, + long timestamp, + boolean allSatisfy, + LongPredicate isDeleted, + PaginationController paginationController); + } + + @FunctionalInterface + public interface PageDataTsPrimitiveValueReader { + + TsPrimitiveType read( + Decoder decoder, ByteBuffer buffer, long timestamp, LongPredicate isDeleted); + } + + @FunctionalInterface + public interface PageDataColumnBuilderValueReader { + + void read( + Decoder decoder, + ByteBuffer buffer, + ColumnBuilder builder, + boolean keepCurrentRow, + boolean isDeleted); + } + + @FunctionalInterface + public interface EmptyValueChunkWriter { + + void write(ValueChunkWriter writer); + } + + public enum PageDataReadStatus { + CONTINUE, + FILTERED, + STOP + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/utils/ValueFilterTypeServices.java b/java/tsfile/src/main/java/org/apache/tsfile/utils/ValueFilterTypeServices.java new file mode 100644 index 000000000..a868ae8a5 --- /dev/null +++ b/java/tsfile/src/main/java/org/apache/tsfile/utils/ValueFilterTypeServices.java @@ -0,0 +1,398 @@ +/* + * 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.tsfile.utils; + +import org.apache.tsfile.common.regexp.LikePattern; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; +import org.apache.tsfile.read.filter.basic.Filter; +import org.apache.tsfile.read.filter.operator.BinaryFilterOperators; +import org.apache.tsfile.read.filter.operator.BooleanFilterOperators; +import org.apache.tsfile.read.filter.operator.DoubleFilterOperators; +import org.apache.tsfile.read.filter.operator.FloatFilterOperators; +import org.apache.tsfile.read.filter.operator.IntegerFilterOperators; +import org.apache.tsfile.read.filter.operator.LongFilterOperators; +import org.apache.tsfile.read.filter.operator.StringFilterOperators; + +import java.util.Set; +import java.util.regex.Pattern; + +@SuppressWarnings("unchecked") +public final class ValueFilterTypeServices { + + public static final TypeService VALUE_GT_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueGt(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueGt(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueGt(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueGt(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueGt(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueGt(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueGt(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_GT_EQ_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueGtEq(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueGtEq(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueGtEq(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueGtEq(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueGtEq(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueGtEq(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueGtEq(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_LT_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueLt(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueLt(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueLt(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueLt(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueLt(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueLt(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueLt(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_LT_EQ_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueLtEq(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueLtEq(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueLtEq(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueLtEq(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueLtEq(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueLtEq(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueLtEq(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_EQ_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueEq(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueEq(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueEq(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueEq(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueEq(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueEq(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueEq(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_NOT_EQ_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value) -> new BooleanFilterOperators.ValueNotEq(index, (boolean) value); + case INT32, DATE -> + (index, value) -> + new IntegerFilterOperators.ValueNotEq(index, ((Number) value).intValue()); + case INT64, TIMESTAMP -> + (index, value) -> + new LongFilterOperators.ValueNotEq(index, ((Number) value).longValue()); + case FLOAT -> + (index, value) -> + new FloatFilterOperators.ValueNotEq(index, ((Number) value).floatValue()); + case DOUBLE -> + (index, value) -> + new DoubleFilterOperators.ValueNotEq(index, ((Number) value).doubleValue()); + case TEXT, BLOB -> + (index, value) -> new BinaryFilterOperators.ValueNotEq(index, (Binary) value); + case STRING -> + (index, value) -> new StringFilterOperators.ValueNotEq(index, (Binary) value); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value) -> unsupportedType(type); + }; + + public static final TypeService VALUE_BETWEEN_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value1, value2) -> + new BooleanFilterOperators.ValueBetweenAnd( + index, (boolean) value1, (boolean) value2); + case INT32, DATE -> + (index, value1, value2) -> + new IntegerFilterOperators.ValueBetweenAnd( + index, ((Number) value1).intValue(), ((Number) value2).intValue()); + case INT64, TIMESTAMP -> + (index, value1, value2) -> + new LongFilterOperators.ValueBetweenAnd( + index, ((Number) value1).longValue(), ((Number) value2).longValue()); + case FLOAT -> + (index, value1, value2) -> + new FloatFilterOperators.ValueBetweenAnd( + index, ((Number) value1).floatValue(), ((Number) value2).floatValue()); + case DOUBLE -> + (index, value1, value2) -> + new DoubleFilterOperators.ValueBetweenAnd( + index, ((Number) value1).doubleValue(), ((Number) value2).doubleValue()); + case TEXT, BLOB -> + (index, value1, value2) -> + new BinaryFilterOperators.ValueBetweenAnd( + index, (Binary) value1, (Binary) value2); + case STRING -> + (index, value1, value2) -> + new StringFilterOperators.ValueBetweenAnd( + index, (Binary) value1, (Binary) value2); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value1, value2) -> unsupportedType(type); + }; + + public static final TypeService VALUE_NOT_BETWEEN_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, value1, value2) -> + new BooleanFilterOperators.ValueNotBetweenAnd( + index, (boolean) value1, (boolean) value2); + case INT32, DATE -> + (index, value1, value2) -> + new IntegerFilterOperators.ValueNotBetweenAnd( + index, ((Number) value1).intValue(), ((Number) value2).intValue()); + case INT64, TIMESTAMP -> + (index, value1, value2) -> + new LongFilterOperators.ValueNotBetweenAnd( + index, ((Number) value1).longValue(), ((Number) value2).longValue()); + case FLOAT -> + (index, value1, value2) -> + new FloatFilterOperators.ValueNotBetweenAnd( + index, ((Number) value1).floatValue(), ((Number) value2).floatValue()); + case DOUBLE -> + (index, value1, value2) -> + new DoubleFilterOperators.ValueNotBetweenAnd( + index, ((Number) value1).doubleValue(), ((Number) value2).doubleValue()); + case TEXT, BLOB -> + (index, value1, value2) -> + new BinaryFilterOperators.ValueNotBetweenAnd( + index, (Binary) value1, (Binary) value2); + case STRING -> + (index, value1, value2) -> + new StringFilterOperators.ValueNotBetweenAnd( + index, (Binary) value1, (Binary) value2); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, value1, value2) -> unsupportedType(type); + }; + + public static final TypeService VALUE_LIKE_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> BooleanFilterOperators.ValueLike::new; + case INT32, DATE -> IntegerFilterOperators.ValueLike::new; + case INT64, TIMESTAMP -> LongFilterOperators.ValueLike::new; + case FLOAT -> FloatFilterOperators.ValueLike::new; + case DOUBLE -> DoubleFilterOperators.ValueLike::new; + case TEXT, BLOB -> BinaryFilterOperators.ValueLike::new; + case STRING -> StringFilterOperators.ValueLike::new; + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, pattern) -> unsupportedType(type); + }; + + public static final TypeService VALUE_NOT_LIKE_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> BooleanFilterOperators.ValueNotLike::new; + case INT32, DATE -> IntegerFilterOperators.ValueNotLike::new; + case INT64, TIMESTAMP -> LongFilterOperators.ValueNotLike::new; + case FLOAT -> FloatFilterOperators.ValueNotLike::new; + case DOUBLE -> DoubleFilterOperators.ValueNotLike::new; + case TEXT, BLOB -> BinaryFilterOperators.ValueNotLike::new; + case STRING -> StringFilterOperators.ValueNotLike::new; + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, pattern) -> unsupportedType(type); + }; + + public static final TypeService VALUE_REGEXP_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> BooleanFilterOperators.ValueRegexp::new; + case INT32, DATE -> IntegerFilterOperators.ValueRegexp::new; + case INT64, TIMESTAMP -> LongFilterOperators.ValueRegexp::new; + case FLOAT -> FloatFilterOperators.ValueRegexp::new; + case DOUBLE -> DoubleFilterOperators.ValueRegexp::new; + case TEXT, BLOB -> BinaryFilterOperators.ValueRegexp::new; + case STRING -> StringFilterOperators.ValueRegexp::new; + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, pattern) -> unsupportedType(type); + }; + + public static final TypeService VALUE_NOT_REGEXP_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> BooleanFilterOperators.ValueNotRegexp::new; + case INT32, DATE -> IntegerFilterOperators.ValueNotRegexp::new; + case INT64, TIMESTAMP -> LongFilterOperators.ValueNotRegexp::new; + case FLOAT -> FloatFilterOperators.ValueNotRegexp::new; + case DOUBLE -> DoubleFilterOperators.ValueNotRegexp::new; + case TEXT, BLOB -> BinaryFilterOperators.ValueNotRegexp::new; + case STRING -> StringFilterOperators.ValueNotRegexp::new; + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, pattern) -> unsupportedType(type); + }; + + public static final TypeService VALUE_IN_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, values) -> new BooleanFilterOperators.ValueIn(index, (Set) values); + case INT32, DATE -> + (index, values) -> new IntegerFilterOperators.ValueIn(index, (Set) values); + case INT64, TIMESTAMP -> + (index, values) -> new LongFilterOperators.ValueIn(index, (Set) values); + case FLOAT -> + (index, values) -> new FloatFilterOperators.ValueIn(index, (Set) values); + case DOUBLE -> + (index, values) -> new DoubleFilterOperators.ValueIn(index, (Set) values); + case TEXT, BLOB -> + (index, values) -> new BinaryFilterOperators.ValueIn(index, (Set) values); + case STRING -> + (index, values) -> new StringFilterOperators.ValueIn(index, (Set) values); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, values) -> unsupportedType(type); + }; + + public static final TypeService VALUE_NOT_IN_FILTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (index, values) -> + new BooleanFilterOperators.ValueNotIn(index, (Set) values); + case INT32, DATE -> + (index, values) -> + new IntegerFilterOperators.ValueNotIn(index, (Set) values); + case INT64, TIMESTAMP -> + (index, values) -> new LongFilterOperators.ValueNotIn(index, (Set) values); + case FLOAT -> + (index, values) -> new FloatFilterOperators.ValueNotIn(index, (Set) values); + case DOUBLE -> + (index, values) -> + new DoubleFilterOperators.ValueNotIn(index, (Set) values); + case TEXT, BLOB -> + (index, values) -> + new BinaryFilterOperators.ValueNotIn(index, (Set) values); + case STRING -> + (index, values) -> + new StringFilterOperators.ValueNotIn(index, (Set) values); + case OBJECT, ROW, UNKNOWN, VECTOR -> (index, values) -> unsupportedType(type); + }; + + static { + VALUE_GT_FILTER_SERVICE.check(); + VALUE_GT_EQ_FILTER_SERVICE.check(); + VALUE_LT_FILTER_SERVICE.check(); + VALUE_LT_EQ_FILTER_SERVICE.check(); + VALUE_EQ_FILTER_SERVICE.check(); + VALUE_NOT_EQ_FILTER_SERVICE.check(); + VALUE_BETWEEN_FILTER_SERVICE.check(); + VALUE_NOT_BETWEEN_FILTER_SERVICE.check(); + VALUE_LIKE_FILTER_SERVICE.check(); + VALUE_NOT_LIKE_FILTER_SERVICE.check(); + VALUE_REGEXP_FILTER_SERVICE.check(); + VALUE_NOT_REGEXP_FILTER_SERVICE.check(); + VALUE_IN_FILTER_SERVICE.check(); + VALUE_NOT_IN_FILTER_SERVICE.check(); + } + + private ValueFilterTypeServices() {} + + private static Filter unsupportedType(Type type) { + throw new UnsupportedOperationException( + Messages.format("error.read.filter_api_unsupported_type", type.getTypeEnum())); + } + + @FunctionalInterface + public interface SingleValueFilterFactory { + Filter create(int measurementIndex, Object value); + } + + @FunctionalInterface + public interface BetweenValueFilterFactory { + Filter create(int measurementIndex, Object value1, Object value2); + } + + @FunctionalInterface + public interface LikeFilterFactory { + Filter create(int measurementIndex, LikePattern pattern); + } + + @FunctionalInterface + public interface RegexpFilterFactory { + Filter create(int measurementIndex, Pattern pattern); + } + + @FunctionalInterface + public interface SetFilterFactory { + Filter create(int measurementIndex, Set values); + } +} diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkGroupWriterImpl.java b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkGroupWriterImpl.java index 07e82767d..8896b58a4 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkGroupWriterImpl.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkGroupWriterImpl.java @@ -31,9 +31,9 @@ import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.i18n.Messages; -import org.apache.tsfile.utils.Binary; -import org.apache.tsfile.utils.DateUtils; -import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.utils.TypeServices; +import org.apache.tsfile.utils.TypeServices.EmptyValueChunkWriter; import org.apache.tsfile.write.record.Tablet; import org.apache.tsfile.write.record.datapoint.DataPoint; import org.apache.tsfile.write.schema.IMeasurementSchema; @@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.time.LocalDate; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -171,34 +170,7 @@ public int write(long time, List data) throws WriteProcessException, if (valueChunkWriter == null) { valueChunkWriter = tryToAddSeriesWriterInternal(point.getMeasurementSchema()); } - switch (point.getType()) { - case BOOLEAN: - valueChunkWriter.write(time, (boolean) point.getValue(), isNull); - break; - case INT32: - case DATE: - valueChunkWriter.write(time, isNull ? 0 : (int) point.getValue(), isNull); - break; - case INT64: - case TIMESTAMP: - valueChunkWriter.write(time, (long) point.getValue(), isNull); - break; - case FLOAT: - valueChunkWriter.write(time, (float) point.getValue(), isNull); - break; - case DOUBLE: - valueChunkWriter.write(time, (double) point.getValue(), isNull); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - valueChunkWriter.write(time, (Binary) point.getValue(), isNull); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", point.getType())); - } + Type.fromTsDataType(point.getType()).write(valueChunkWriter, time, point.getValue(), isNull); } if (!emptyValueChunkWriters.isEmpty()) { writeEmptyDataInOneRow(emptyValueChunkWriters); @@ -255,45 +227,8 @@ public int write(Tablet tablet, int startRowIndex, int endRowIndex) // check isNull by bitMap in tablet ValueChunkWriter valueChunkWriter = tryToAddSeriesWriterInternal(measurementSchemas.get(columnIndex)); - switch (measurementSchemas.get(columnIndex).getType()) { - case BOOLEAN: - valueChunkWriter.write( - time, ((boolean[]) tablet.getValues()[columnIndex])[row], isNull); - break; - case INT32: - valueChunkWriter.write(time, ((int[]) tablet.getValues()[columnIndex])[row], isNull); - break; - case DATE: - valueChunkWriter.write( - time, - isNull - ? 0 - : DateUtils.parseDateExpressionToInt( - ((LocalDate[]) tablet.getValues()[columnIndex])[row]), - isNull); - break; - case INT64: - case TIMESTAMP: - valueChunkWriter.write(time, ((long[]) tablet.getValues()[columnIndex])[row], isNull); - break; - case FLOAT: - valueChunkWriter.write(time, ((float[]) tablet.getValues()[columnIndex])[row], isNull); - break; - case DOUBLE: - valueChunkWriter.write(time, ((double[]) tablet.getValues()[columnIndex])[row], isNull); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - valueChunkWriter.write(time, ((Binary[]) tablet.getValues()[columnIndex])[row], isNull); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format( - "error.write.type_not_supported", - measurementSchemas.get(columnIndex).getType())); - } + Type.fromTsDataType(measurementSchemas.get(columnIndex).getType()) + .write(valueChunkWriter, time, tablet.getValues()[columnIndex], row, isNull); } // TODO: we can write the null columns after whole insertion, according to the point number // in the time chunk before and after, no need to do it in a row-by-row manner @@ -357,35 +292,10 @@ public void tryToAddEmptyPageAndData(ValueChunkWriter valueChunkWriter) throws I private void writeEmptyDataInOneRow(List valueChunkWriterList) { for (ValueChunkWriter valueChunkWriter : valueChunkWriterList) { - TSDataType dataType = valueChunkWriter.getDataType(); - switch (dataType) { - case BOOLEAN: - valueChunkWriter.write(-1, false, true); - break; - case INT32: - case DATE: - valueChunkWriter.write(-1, 0, true); - break; - case INT64: - case TIMESTAMP: - valueChunkWriter.write(-1, 0L, true); - break; - case FLOAT: - valueChunkWriter.write(-1, 0.0f, true); - break; - case DOUBLE: - valueChunkWriter.write(-1, 0.0d, true); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - valueChunkWriter.write(-1, null, true); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } + EmptyValueChunkWriter emptyValueWriter = + TypeServices.WRITE_EMPTY_VALUE_TO_CHUNK_SERVICE.call( + Type.fromTsDataType(valueChunkWriter.getDataType())); + emptyValueWriter.write(valueChunkWriter); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkWriterImpl.java b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkWriterImpl.java index 17c74cf62..ecdf14151 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkWriterImpl.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/AlignedChunkWriterImpl.java @@ -29,8 +29,8 @@ import org.apache.tsfile.file.header.PageHeader; import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; -import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.block.column.TimeColumn; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.schema.IMeasurementSchema; @@ -39,7 +39,6 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -48,6 +47,7 @@ public class AlignedChunkWriterImpl implements IChunkWriter { protected TimeChunkWriter timeChunkWriter; protected List valueChunkWriterList; + private Type[] valueTypes; protected int valueIndex; protected EncryptParameter encryptParam; @@ -303,41 +303,24 @@ public void write(long time, Binary value, boolean isNull, int valueIndex) { } public void write(long time, TsPrimitiveType[] points) { + if (valueTypes == null) { + initValueTypes(); + } valueIndex = 0; for (TsPrimitiveType point : points) { ValueChunkWriter writer = valueChunkWriterList.get(valueIndex++); - switch (writer.getDataType()) { - case INT64: - case TIMESTAMP: - writer.write(time, point != null ? point.getLong() : Long.MAX_VALUE, point == null); - break; - case INT32: - case DATE: - writer.write(time, point != null ? point.getInt() : Integer.MAX_VALUE, point == null); - break; - case FLOAT: - writer.write(time, point != null ? point.getFloat() : Float.MAX_VALUE, point == null); - break; - case DOUBLE: - writer.write(time, point != null ? point.getDouble() : Double.MAX_VALUE, point == null); - break; - case BOOLEAN: - writer.write(time, point != null ? point.getBoolean() : false, point == null); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - writer.write( - time, - point != null ? point.getBinary() : new Binary("".getBytes(StandardCharsets.UTF_8)), - point == null); - break; - } + valueTypes[valueIndex - 1].write(writer, time, point); } write(time); } + private void initValueTypes() { + valueTypes = new Type[valueChunkWriterList.size()]; + for (int i = 0; i < valueChunkWriterList.size(); i++) { + valueTypes[i] = Type.fromTsDataType(valueChunkWriterList.get(i).getDataType()); + } + } + public void write(long time) { valueIndex = 0; timeChunkWriter.write(time); @@ -351,6 +334,9 @@ public void writeTime(long time) { } public void write(TimeColumn timeColumn, Column[] valueColumns, int batchSize) { + if (valueTypes == null) { + initValueTypes(); + } if (remainingPointsNumber < batchSize) { int pointsHasWritten = (int) remainingPointsNumber; batchWrite(timeColumn, valueColumns, pointsHasWritten, 0); @@ -367,35 +353,7 @@ private void batchWrite( for (Column column : valueColumns) { ValueChunkWriter chunkWriter = valueChunkWriterList.get(valueIndex++); - TSDataType tsDataType = chunkWriter.getDataType(); - switch (tsDataType) { - case TEXT: - case BLOB: - case STRING: - case OBJECT: - chunkWriter.write(times, column.getBinaries(), column.isNull(), batchSize, arrayOffset); - break; - case DOUBLE: - chunkWriter.write(times, column.getDoubles(), column.isNull(), batchSize, arrayOffset); - break; - case BOOLEAN: - chunkWriter.write(times, column.getBooleans(), column.isNull(), batchSize, arrayOffset); - break; - case INT64: - case TIMESTAMP: - chunkWriter.write(times, column.getLongs(), column.isNull(), batchSize, arrayOffset); - break; - case INT32: - case DATE: - chunkWriter.write(times, column.getInts(), column.isNull(), batchSize, arrayOffset); - break; - case FLOAT: - chunkWriter.write(times, column.getFloats(), column.isNull(), batchSize, arrayOffset); - break; - default: - throw new UnsupportedOperationException( - Messages.format("error.write.chunk_unknown_type", tsDataType)); - } + valueTypes[valueIndex - 1].write(chunkWriter, times, column, batchSize, arrayOffset); } write(times, batchSize, arrayOffset); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/NonAlignedChunkGroupWriterImpl.java b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/NonAlignedChunkGroupWriterImpl.java index b525064fa..ea73884d2 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/NonAlignedChunkGroupWriterImpl.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/chunk/NonAlignedChunkGroupWriterImpl.java @@ -22,13 +22,10 @@ import org.apache.tsfile.encrypt.EncryptParameter; import org.apache.tsfile.encrypt.EncryptUtils; import org.apache.tsfile.enums.ColumnCategory; -import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.i18n.Messages; -import org.apache.tsfile.utils.Binary; -import org.apache.tsfile.utils.DateUtils; -import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.write.record.Tablet; import org.apache.tsfile.write.record.datapoint.DataPoint; import org.apache.tsfile.write.schema.IMeasurementSchema; @@ -38,7 +35,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.time.LocalDate; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -118,7 +114,8 @@ public int write(Tablet tablet, int startRowIndex, int endRowIndex) continue; } String measurementId = timeseries.get(column).getMeasurementName(); - TSDataType tsDataType = timeseries.get(column).getType(); + Type type = Type.fromTsDataType(timeseries.get(column).getType()); + ChunkWriterImpl chunkWriter = chunkWriters.get(measurementId); pointCount = 0; for (int row = startRowIndex; row < endRowIndex; row++) { // check isNull in tablet @@ -130,49 +127,7 @@ public int write(Tablet tablet, int startRowIndex, int endRowIndex) long time = tablet.getTimestamps()[row]; checkIsHistoryData(measurementId, time); pointCount++; - switch (tsDataType) { - case INT32: - chunkWriters.get(measurementId).write(time, ((int[]) tablet.getValues()[column])[row]); - break; - case DATE: - chunkWriters - .get(measurementId) - .write( - time, - DateUtils.parseDateExpressionToInt( - ((LocalDate[]) tablet.getValues()[column])[row])); - break; - case INT64: - case TIMESTAMP: - chunkWriters.get(measurementId).write(time, ((long[]) tablet.getValues()[column])[row]); - break; - case FLOAT: - chunkWriters - .get(measurementId) - .write(time, ((float[]) tablet.getValues()[column])[row]); - break; - case DOUBLE: - chunkWriters - .get(measurementId) - .write(time, ((double[]) tablet.getValues()[column])[row]); - break; - case BOOLEAN: - chunkWriters - .get(measurementId) - .write(time, ((boolean[]) tablet.getValues()[column])[row]); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - chunkWriters - .get(measurementId) - .write(time, ((Binary[]) tablet.getValues()[column])[row]); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", tsDataType)); - } + type.write(chunkWriter, time, tablet.getValues()[column], row); lastTimeMap.put(measurementId, time); } maxPointCount = Math.max(pointCount, maxPointCount); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/record/Tablet.java b/java/tsfile/src/main/java/org/apache/tsfile/write/record/Tablet.java index 2bad6c953..843588c59 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/record/Tablet.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/record/Tablet.java @@ -28,15 +28,14 @@ import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.StringArrayDeviceID; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Accountable; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BitMap; import org.apache.tsfile.utils.BytesUtils; -import org.apache.tsfile.utils.DateUtils; import org.apache.tsfile.utils.PublicBAOS; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; -import org.apache.tsfile.write.UnSupportedDataTypeException; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.apache.tsfile.write.schema.MeasurementSchema; @@ -67,7 +66,6 @@ public class Tablet implements Accountable { private static final long TABLET_SIZE = RamUsageEstimator.shallowSizeOfInstance(Tablet.class); private static final int DEFAULT_SIZE = 1024; - private static final LocalDate EMPTY_DATE = LocalDate.of(1000, 1, 1); /** DeviceId if using tree-view interfaces or TableName when using table-view interfaces. */ private String insertTargetName; @@ -310,120 +308,7 @@ private void addValueOfDataType( // Mark the null value position updateBitMap(rowIndex, indexOfSchema, value == null); - switch (dataType) { - case TEXT: - case STRING: - case BLOB: - case OBJECT: - { - if (value != null && !(value instanceof Binary) && !(value instanceof String)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Binary or String", - dataType, - value.getClass().getName())); - } - final Binary[] sensor = (Binary[]) values[indexOfSchema]; - if (value instanceof Binary) { - sensor[rowIndex] = (Binary) value; - } else { - sensor[rowIndex] = - value != null - ? new Binary(((String) value).getBytes(TSFileConfig.STRING_CHARSET)) - : Binary.EMPTY_VALUE; - } - break; - } - case FLOAT: - { - if (value != null && !(value instanceof Float)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Float", - dataType, - value.getClass().getName())); - } - final float[] sensor = (float[]) values[indexOfSchema]; - sensor[rowIndex] = value != null ? (float) value : Float.MIN_VALUE; - break; - } - case INT32: - { - if (value != null && !(value instanceof Integer)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Integer", - dataType, - value.getClass().getName())); - } - final int[] sensor = (int[]) values[indexOfSchema]; - sensor[rowIndex] = value != null ? (int) value : Integer.MIN_VALUE; - break; - } - case DATE: - { - if (value != null && !(value instanceof LocalDate)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "LocalDate", - dataType, - value.getClass().getName())); - } - final LocalDate[] sensor = (LocalDate[]) values[indexOfSchema]; - sensor[rowIndex] = value != null ? (LocalDate) value : EMPTY_DATE; - break; - } - case INT64: - case TIMESTAMP: - { - if (value != null && !(value instanceof Long)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Long", - dataType, - value.getClass().getName())); - } - final long[] sensor = (long[]) values[indexOfSchema]; - sensor[rowIndex] = value != null ? (long) value : Long.MIN_VALUE; - break; - } - case DOUBLE: - { - if (value != null && !(value instanceof Double)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Double", - dataType, - value.getClass().getName())); - } - final double[] sensor = (double[]) values[indexOfSchema]; - sensor[rowIndex] = value != null ? (double) value : Double.MIN_VALUE; - break; - } - case BOOLEAN: - { - if (value != null && !(value instanceof Boolean)) { - throw new IllegalArgumentException( - Messages.format( - "error.write.tablet_expected_type", - "Boolean", - dataType, - value.getClass().getName())); - } - final boolean[] sensor = (boolean[]) values[indexOfSchema]; - sensor[rowIndex] = value != null && (boolean) value; - break; - } - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } + Type.fromTsDataType(dataType).addValue(rowIndex, value, values[indexOfSchema]); } @TsFileApi @@ -711,39 +596,7 @@ private Object createValueColumnOfDataType(TSDataType dataType) { } private Object createValueColumnOfDataType(TSDataType dataType, int capacity) { - - Object valueColumn; - switch (dataType) { - case INT32: - valueColumn = new int[capacity]; - break; - case INT64: - case TIMESTAMP: - valueColumn = new long[capacity]; - break; - case FLOAT: - valueColumn = new float[capacity]; - break; - case DOUBLE: - valueColumn = new double[capacity]; - break; - case BOOLEAN: - valueColumn = new boolean[capacity]; - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - valueColumn = new Binary[capacity]; - break; - case DATE: - valueColumn = new LocalDate[capacity]; - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } - return valueColumn; + return Type.fromTsDataType(dataType).createArray(capacity); } /** Serialize {@link Tablet} */ @@ -824,55 +677,21 @@ private int serializedSizeOfValues() { if (values != null) { final int columnCount = schemas == null ? 0 : schemas.size(); for (int i = 0; i < columnCount; i++) { - size = Math.addExact(size, serializedSizeOfColumn(schemas.get(i).getType(), values[i])); + size = + Math.addExact( + size, + serializedSizeOfColumn(Type.fromTsDataType(schemas.get(i).getType()), values[i])); } } return size; } - private int serializedSizeOfColumn(final TSDataType dataType, final Object column) { + private int serializedSizeOfColumn(final Type type, final Object column) { int size = Byte.BYTES; if (column == null) { return size; } - switch (dataType) { - case INT32: - return Math.addExact(size, Math.multiplyExact(Integer.BYTES, rowSize)); - case DATE: - return Math.addExact(size, Math.multiplyExact(Integer.BYTES, rowSize)); - case INT64: - case TIMESTAMP: - return Math.addExact(size, Math.multiplyExact(Long.BYTES, rowSize)); - case FLOAT: - return Math.addExact(size, Math.multiplyExact(Float.BYTES, rowSize)); - case DOUBLE: - return Math.addExact(size, Math.multiplyExact(Double.BYTES, rowSize)); - case BOOLEAN: - return Math.addExact(size, rowSize); - case TEXT: - case STRING: - case BLOB: - case OBJECT: - return Math.addExact(size, serializedSizeOfBinaryValues((Binary[]) column)); - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } - } - - private static int serializedSizeOfBinaryValues(final Binary[] binaryValues, final int rowSize) { - int size = 0; - for (int j = 0; j < rowSize; j++) { - size = Math.addExact(size, Byte.BYTES); - if (binaryValues[j] != null) { - size = Math.addExact(size, ReadWriteIOUtils.sizeToWrite(binaryValues[j])); - } - } - return size; - } - - private int serializedSizeOfBinaryValues(final Binary[] binaryValues) { - return serializedSizeOfBinaryValues(binaryValues, rowSize); + return Math.addExact(size, type.serializedSize(column, rowSize)); } /** Serialize {@link MeasurementSchema}s */ @@ -926,75 +745,17 @@ private void writeValues(DataOutputStream stream) throws IOException { if (values != null) { int size = (schemas == null ? 0 : schemas.size()); for (int i = 0; i < size; i++) { - serializeColumn(schemas.get(i).getType(), values[i], stream, columnCategories.get(i)); + serializeColumn(Type.fromTsDataType(schemas.get(i).getType()), values[i], stream); } } } - private void serializeColumn( - TSDataType dataType, Object column, DataOutputStream stream, ColumnCategory columnCategory) + private void serializeColumn(Type type, Object column, DataOutputStream stream) throws IOException { ReadWriteIOUtils.write(BytesUtils.boolToByte(column != null), stream); if (column != null) { - switch (dataType) { - case INT32: - int[] intValues = (int[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(intValues[j], stream); - } - break; - case DATE: - LocalDate[] dateValues = (LocalDate[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write( - dateValues[j] == null - ? DateUtils.EMPTY_DATE_INT - : DateUtils.parseDateExpressionToInt(dateValues[j]), - stream); - } - break; - case INT64: - case TIMESTAMP: - long[] longValues = (long[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(longValues[j], stream); - } - break; - case FLOAT: - float[] floatValues = (float[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(floatValues[j], stream); - } - break; - case DOUBLE: - double[] doubleValues = (double[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(doubleValues[j], stream); - } - break; - case BOOLEAN: - boolean[] boolValues = (boolean[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(BytesUtils.boolToByte(boolValues[j]), stream); - } - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - Binary[] binaryValues = (Binary[]) column; - for (int j = 0; j < rowSize; j++) { - ReadWriteIOUtils.write(BytesUtils.boolToByte(binaryValues[j] != null), stream); - if (binaryValues[j] != null) { - ReadWriteIOUtils.write(binaryValues[j], stream); - } - } - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } + type.serializeArray(column, rowSize, stream); } } @@ -1081,70 +842,7 @@ public static Object[] readvaluesFromBuffer( boolean isValueColumnsNotNull = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(byteBuffer)); if (isValueColumnsNotNull) { - switch (types[i]) { - case BOOLEAN: - boolean[] boolValues = new boolean[rowSize]; - for (int index = 0; index < rowSize; index++) { - boolValues[index] = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(byteBuffer)); - } - values[i] = boolValues; - break; - case INT32: - int[] intValues = new int[rowSize]; - for (int index = 0; index < rowSize; index++) { - intValues[index] = ReadWriteIOUtils.readInt(byteBuffer); - } - values[i] = intValues; - break; - case DATE: - LocalDate[] dateValues = new LocalDate[rowSize]; - for (int index = 0; index < rowSize; index++) { - dateValues[index] = - DateUtils.parseIntToLocalDate(ReadWriteIOUtils.readInt(byteBuffer)); - } - values[i] = dateValues; - break; - case INT64: - case TIMESTAMP: - long[] longValues = new long[rowSize]; - for (int index = 0; index < rowSize; index++) { - longValues[index] = ReadWriteIOUtils.readLong(byteBuffer); - } - values[i] = longValues; - break; - case FLOAT: - float[] floatValues = new float[rowSize]; - for (int index = 0; index < rowSize; index++) { - floatValues[index] = ReadWriteIOUtils.readFloat(byteBuffer); - } - values[i] = floatValues; - break; - case DOUBLE: - double[] doubleValues = new double[rowSize]; - for (int index = 0; index < rowSize; index++) { - doubleValues[index] = ReadWriteIOUtils.readDouble(byteBuffer); - } - values[i] = doubleValues; - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - Binary[] binaryValues = new Binary[rowSize]; - for (int index = 0; index < rowSize; index++) { - boolean isNotNull = BytesUtils.byteToBool(ReadWriteIOUtils.readByte(byteBuffer)); - if (isNotNull) { - binaryValues[index] = ReadWriteIOUtils.readBinary(byteBuffer); - } else { - binaryValues[index] = Binary.EMPTY_VALUE; - } - } - values[i] = binaryValues; - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.tablet_client_type_not_supported", types[i])); - } + values[i] = Type.fromTsDataType(types[i]).deserializeArray(byteBuffer, rowSize); } } return values; @@ -1209,98 +907,9 @@ public boolean equals(Object o) { return false; } - switch (schemas.get(i).getType()) { - case INT32: - int[] thisIntValues = (int[]) values[i]; - int[] thatIntValues = (int[]) thatValues[i]; - if (thisIntValues.length < rowSize || thatIntValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (thisIntValues[j] != thatIntValues[j]) { - return false; - } - } - break; - case DATE: - LocalDate[] thisDateValues = (LocalDate[]) values[i]; - LocalDate[] thatDateValues = (LocalDate[]) thatValues[i]; - if (thisDateValues.length < rowSize || thatDateValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (!thisDateValues[j].equals(thatDateValues[j])) { - return false; - } - } - break; - case INT64: - case TIMESTAMP: - long[] thisLongValues = (long[]) values[i]; - long[] thatLongValues = (long[]) thatValues[i]; - if (thisLongValues.length < rowSize || thatLongValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (thisLongValues[j] != thatLongValues[j]) { - return false; - } - } - break; - case FLOAT: - float[] thisFloatValues = (float[]) values[i]; - float[] thatFloatValues = (float[]) thatValues[i]; - if (thisFloatValues.length < rowSize || thatFloatValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (thisFloatValues[j] != thatFloatValues[j]) { - return false; - } - } - break; - case DOUBLE: - double[] thisDoubleValues = (double[]) values[i]; - double[] thatDoubleValues = (double[]) thatValues[i]; - if (thisDoubleValues.length < rowSize || thatDoubleValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (thisDoubleValues[j] != thatDoubleValues[j]) { - return false; - } - } - break; - case BOOLEAN: - boolean[] thisBooleanValues = (boolean[]) values[i]; - boolean[] thatBooleanValues = (boolean[]) thatValues[i]; - if (thisBooleanValues.length < rowSize || thatBooleanValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (thisBooleanValues[j] != thatBooleanValues[j]) { - return false; - } - } - break; - case TEXT: - case STRING: - case BLOB: - case OBJECT: - Binary[] thisBinaryValues = (Binary[]) values[i]; - Binary[] thatBinaryValues = (Binary[]) thatValues[i]; - if (thisBinaryValues.length < rowSize || thatBinaryValues.length < rowSize) { - return false; - } - for (int j = 0; j < rowSize; j++) { - if (!thisBinaryValues[j].equals(thatBinaryValues[j])) { - return false; - } - } - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", schemas.get(i).getType())); + if (!Type.fromTsDataType(schemas.get(i).getType()) + .arrayEquals(values[i], thatValues[i], rowSize)) { + return false; } } @@ -1378,29 +987,7 @@ public Object getValue(int i, int j) { if (isNull(i, j)) { return null; } - switch (schemas.get(j).getType()) { - case BLOB: - case TEXT: - case STRING: - case OBJECT: - return ((Binary[]) values[j])[i]; - case INT32: - return ((int[]) values[j])[i]; - case FLOAT: - return ((float[]) values[j])[i]; - case DOUBLE: - return ((double[]) values[j])[i]; - case BOOLEAN: - return ((boolean[]) values[j])[i]; - case INT64: - case TIMESTAMP: - return ((long[]) values[j])[i]; - case DATE: - return ((LocalDate[]) values[j])[i]; - default: - throw new IllegalArgumentException( - Messages.format("error.write.tablet_unsupported_type", schemas.get(j).getType())); - } + return Type.fromTsDataType(schemas.get(j).getType()).getValue(values[j], i); } /** @@ -1700,33 +1287,7 @@ public long ramBytesUsed() { if (values == null || values.length <= column) { continue; } - switch (tsDataType) { - case INT64: - case TIMESTAMP: - totalSizeInBytes += RamUsageEstimator.sizeOf((long[]) values[column]); - break; - case DATE: - totalSizeInBytes += RamUsageEstimator.sizeOf((LocalDate[]) values[column]); - break; - case INT32: - totalSizeInBytes += RamUsageEstimator.sizeOf((int[]) values[column]); - break; - case DOUBLE: - totalSizeInBytes += RamUsageEstimator.sizeOf((double[]) values[column]); - break; - case FLOAT: - totalSizeInBytes += RamUsageEstimator.sizeOf((float[]) values[column]); - break; - case BOOLEAN: - totalSizeInBytes += RamUsageEstimator.sizeOf((boolean[]) values[column]); - break; - case STRING: - case TEXT: - case BLOB: - case OBJECT: - totalSizeInBytes += RamUsageEstimator.sizeOf((Binary[]) values[column]); - break; - } + totalSizeInBytes += Type.fromTsDataType(tsDataType).estimateArraySize(values[column]); } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/record/datapoint/DataPoint.java b/java/tsfile/src/main/java/org/apache/tsfile/write/record/datapoint/DataPoint.java index 103e7503d..388f6aa3b 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/record/datapoint/DataPoint.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/record/datapoint/DataPoint.java @@ -19,11 +19,10 @@ package org.apache.tsfile.write.record.datapoint; -import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; -import org.apache.tsfile.utils.DateUtils; import org.apache.tsfile.utils.StringContainer; import org.apache.tsfile.write.UnSupportedDataTypeException; import org.apache.tsfile.write.chunk.ChunkWriterImpl; @@ -67,46 +66,13 @@ protected DataPoint(TSDataType type, String measurementId) { * @return data point class according to data type */ public static DataPoint getDataPoint(TSDataType dataType, String measurementId, String value) { - DataPoint dataPoint = null; try { - switch (dataType) { - case INT32: - dataPoint = new IntDataPoint(measurementId, Integer.parseInt(value)); - break; - case DATE: - dataPoint = new IntDataPoint(measurementId, DateUtils.parseDateExpressionToInt(value)); - break; - case INT64: - case TIMESTAMP: - dataPoint = new LongDataPoint(measurementId, Long.parseLong(value)); - break; - case FLOAT: - dataPoint = new FloatDataPoint(measurementId, Float.parseFloat(value)); - break; - case DOUBLE: - dataPoint = new DoubleDataPoint(measurementId, Double.parseDouble(value)); - break; - case BOOLEAN: - dataPoint = new BooleanDataPoint(measurementId, Boolean.parseBoolean(value)); - break; - case TEXT: - case BLOB: - case STRING: - case OBJECT: - dataPoint = - new StringDataPoint(measurementId, new Binary(value, TSFileConfig.STRING_CHARSET)); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format("error.write.type_not_supported", dataType)); - } + return Type.fromTsDataType(dataType).getDataPoint(measurementId, value); } catch (Exception e) { throw new UnSupportedDataTypeException( Messages.format( "error.write.datapoint_type_value_mismatch", measurementId, dataType, value)); } - - return dataPoint; } /** diff --git a/java/tsfile/src/main/java/org/apache/tsfile/write/v4/TableTsBlock2TsFileWriter.java b/java/tsfile/src/main/java/org/apache/tsfile/write/v4/TableTsBlock2TsFileWriter.java index 8e1561864..6c0503832 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/write/v4/TableTsBlock2TsFileWriter.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/write/v4/TableTsBlock2TsFileWriter.java @@ -28,8 +28,8 @@ import org.apache.tsfile.i18n.Messages; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TimeColumn; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Pair; -import org.apache.tsfile.write.UnSupportedDataTypeException; import org.apache.tsfile.write.chunk.AlignedChunkGroupWriterImpl; import org.apache.tsfile.write.chunk.IChunkGroupWriter; import org.apache.tsfile.write.chunk.TableChunkGroupWriterImpl; @@ -198,13 +198,16 @@ public int getRowCount() { */ private class TableTsBlockChunkGroupWriterImpl extends TableChunkGroupWriterImpl { private final ValueChunkWriter[] valueChunkWriters; + private final Type[] valueTypes; public TableTsBlockChunkGroupWriterImpl(IDeviceID deviceId) throws IOException { super(deviceId); // Initialize ValueChunkWriter for each measurement this.valueChunkWriters = new ValueChunkWriter[fieldColumnSchemas.length]; + this.valueTypes = new Type[fieldColumnSchemas.length]; for (int i = 0; i < fieldColumnSchemas.length; i++) { valueChunkWriters[i] = tryToAddSeriesWriterInternal(fieldColumnSchemas[i]); + valueTypes[i] = Type.fromTsDataType(valueChunkWriters[i].getDataType()); } } @@ -227,35 +230,7 @@ public int write(Column timeColumn, Column[] valueColumns, int startRowIndex, in Column valueColumn = valueColumns[valueColumnIndex]; ValueChunkWriter valueChunkWriter = valueChunkWriters[valueColumnIndex]; boolean isNull = valueColumn.isNull(rowIndex); - switch (valueChunkWriter.getDataType()) { - case BOOLEAN: - valueChunkWriter.write( - time, isNull ? false : valueColumn.getBoolean(rowIndex), isNull); - break; - case INT32: - case DATE: - valueChunkWriter.write(time, isNull ? 0 : valueColumn.getInt(rowIndex), isNull); - break; - case INT64: - case TIMESTAMP: - valueChunkWriter.write(time, isNull ? 0 : valueColumn.getLong(rowIndex), isNull); - break; - case FLOAT: - valueChunkWriter.write(time, isNull ? 0 : valueColumn.getFloat(rowIndex), isNull); - break; - case DOUBLE: - valueChunkWriter.write(time, isNull ? 0 : valueColumn.getDouble(rowIndex), isNull); - break; - case TEXT: - case BLOB: - case STRING: - valueChunkWriter.write(time, isNull ? null : valueColumn.getBinary(rowIndex), isNull); - break; - default: - throw new UnSupportedDataTypeException( - Messages.format( - "error.write.type_not_supported", valueChunkWriter.getDataType().getType())); - } + valueTypes[valueColumnIndex].write(valueChunkWriter, time, valueColumn, rowIndex, isNull); } timeChunkWriter.write(time); lastTime = time; diff --git a/java/tsfile/src/test/java/org/apache/tsfile/common/block/ColumnSerializeWithoutNullsTest.java b/java/tsfile/src/test/java/org/apache/tsfile/common/block/ColumnSerializeWithoutNullsTest.java new file mode 100644 index 000000000..c412a8b9c --- /dev/null +++ b/java/tsfile/src/test/java/org/apache/tsfile/common/block/ColumnSerializeWithoutNullsTest.java @@ -0,0 +1,114 @@ +/* + * 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.tsfile.common.block; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.read.common.block.column.BinaryColumn; +import org.apache.tsfile.read.common.block.column.BooleanColumn; +import org.apache.tsfile.read.common.block.column.DoubleColumn; +import org.apache.tsfile.read.common.block.column.FloatColumn; +import org.apache.tsfile.read.common.block.column.IntColumn; +import org.apache.tsfile.read.common.block.column.LongColumn; +import org.apache.tsfile.read.common.block.column.NullColumn; +import org.apache.tsfile.read.common.block.column.TimeColumn; +import org.apache.tsfile.utils.Binary; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +public class ColumnSerializeWithoutNullsTest { + + private static final boolean[] NULL_INDICATORS = {false, true, false, true}; + + @Test + public void testSerializeWithoutNulls() throws IOException { + ByteBuffer buffer = + serialize( + new BooleanColumn( + 4, Optional.of(NULL_INDICATORS), new boolean[] {true, true, false, true})); + Assert.assertEquals((byte) 0b1000_0000, buffer.get()); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = serialize(new IntColumn(4, Optional.of(NULL_INDICATORS), new int[] {1, 2, 3, 4})); + Assert.assertEquals(1, buffer.getInt()); + Assert.assertEquals(3, buffer.getInt()); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = + serialize(new LongColumn(4, Optional.of(NULL_INDICATORS), new long[] {1L, 2L, 3L, 4L})); + Assert.assertEquals(1L, buffer.getLong()); + Assert.assertEquals(3L, buffer.getLong()); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = + serialize(new FloatColumn(4, Optional.of(NULL_INDICATORS), new float[] {1F, 2F, 3F, 4F})); + Assert.assertEquals(1F, Float.intBitsToFloat(buffer.getInt()), 0); + Assert.assertEquals(3F, Float.intBitsToFloat(buffer.getInt()), 0); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = + serialize(new DoubleColumn(4, Optional.of(NULL_INDICATORS), new double[] {1D, 2D, 3D, 4D})); + Assert.assertEquals(1D, Double.longBitsToDouble(buffer.getLong()), 0); + Assert.assertEquals(3D, Double.longBitsToDouble(buffer.getLong()), 0); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = + serialize( + new BinaryColumn( + 4, + Optional.of(NULL_INDICATORS), + new Binary[] {binary("a"), binary("b"), binary("cc"), binary("d")})); + Assert.assertEquals(binary("a"), readBinary(buffer)); + Assert.assertEquals(binary("cc"), readBinary(buffer)); + Assert.assertFalse(buffer.hasRemaining()); + + buffer = serialize(new TimeColumn(4, new long[] {1L, 2L, 3L, 4L})); + Assert.assertEquals(1L, buffer.getLong()); + Assert.assertEquals(2L, buffer.getLong()); + Assert.assertEquals(3L, buffer.getLong()); + Assert.assertEquals(4L, buffer.getLong()); + Assert.assertFalse(buffer.hasRemaining()); + + Assert.assertFalse(serialize(new NullColumn(4)).hasRemaining()); + } + + private static ByteBuffer serialize(Column column) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + column.serializeWithoutNulls(new DataOutputStream(output)); + return ByteBuffer.wrap(output.toByteArray()); + } + + private static Binary binary(String value) { + return new Binary(value, StandardCharsets.UTF_8); + } + + private static Binary readBinary(ByteBuffer buffer) { + byte[] value = new byte[buffer.getInt()]; + buffer.get(value); + return new Binary(value); + } +} diff --git a/java/tsfile/src/test/java/org/apache/tsfile/common/block/Int32ArrayColumnEncoderTest.java b/java/tsfile/src/test/java/org/apache/tsfile/common/block/Int32ArrayColumnEncoderTest.java index 8c82a11dc..51f6c833d 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/common/block/Int32ArrayColumnEncoderTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/common/block/Int32ArrayColumnEncoderTest.java @@ -37,6 +37,13 @@ import java.util.Random; public class Int32ArrayColumnEncoderTest { + + @Test(expected = IllegalArgumentException.class) + public void testInvalidDataType() { + ColumnEncoderFactory.get(ColumnEncoding.INT32_ARRAY) + .readColumn(ByteBuffer.allocate(0), TSDataType.DOUBLE, 0); + } + @Test public void testIntColumn() { final int positionCount = 10; diff --git a/java/tsfile/src/test/java/org/apache/tsfile/common/block/NullColumnUnitTest.java b/java/tsfile/src/test/java/org/apache/tsfile/common/block/NullColumnUnitTest.java index 1c0fa5b2b..b1fcb3e8d 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/common/block/NullColumnUnitTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/common/block/NullColumnUnitTest.java @@ -22,12 +22,46 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.column.NullColumn; +import org.apache.tsfile.read.common.type.Type; import org.junit.Assert; import org.junit.Test; public class NullColumnUnitTest { + @Test + public void testCreatingNullColumnsByType() { + Object[][] testCases = { + {TSDataType.BOOLEAN, TSDataType.BOOLEAN}, + {TSDataType.INT32, TSDataType.INT32}, + {TSDataType.DATE, TSDataType.INT32}, + {TSDataType.INT64, TSDataType.INT64}, + {TSDataType.TIMESTAMP, TSDataType.INT64}, + {TSDataType.FLOAT, TSDataType.FLOAT}, + {TSDataType.DOUBLE, TSDataType.DOUBLE}, + {TSDataType.TEXT, TSDataType.TEXT}, + {TSDataType.STRING, TSDataType.TEXT}, + {TSDataType.BLOB, TSDataType.TEXT}, + {TSDataType.OBJECT, TSDataType.TEXT} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + TSDataType expectedDataType = (TSDataType) testCase[1]; + assertNullColumn(Type.fromTsDataType(dataType).createNullColumn(10), expectedDataType); + assertNullColumn(NullColumn.create(dataType, 10), expectedDataType); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + NullColumn.create(dataType, 10); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ignored) { + // Expected. + } + } + } + @Test public void testCreatingBooleanNullColumn() { Column nullColumn = NullColumn.create(TSDataType.BOOLEAN, 10); @@ -87,4 +121,12 @@ public void testCreatingDoubleNullColumn() { Assert.assertTrue(nullColumn.isNull(0)); Assert.assertTrue(nullColumn.isNull(9)); } + + private static void assertNullColumn(Column column, TSDataType expectedDataType) { + Assert.assertEquals(expectedDataType, column.getDataType()); + Assert.assertEquals(10, column.getPositionCount()); + Assert.assertTrue(column.mayHaveNull()); + Assert.assertTrue(column.isNull(0)); + Assert.assertTrue(column.isNull(9)); + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/common/conf/TSFileDescriptorTest.java b/java/tsfile/src/test/java/org/apache/tsfile/common/conf/TSFileDescriptorTest.java index 1dc6214d7..668fee698 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/common/conf/TSFileDescriptorTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/common/conf/TSFileDescriptorTest.java @@ -19,6 +19,9 @@ package org.apache.tsfile.common.conf; import org.apache.tsfile.common.constant.TsFileConstant; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.junit.Assert; import org.junit.Before; @@ -82,4 +85,57 @@ public void testGetInstanceOverWriteFromFile() { Assert.assertEquals(tsFileConfig1.getMaxStringLength(), tsFileConfig2.getMaxStringLength()); } + + @Test + public void testGetValueEncoder() { + tsFileConfig1.setBooleanEncoding(TSEncoding.PLAIN.name()); + tsFileConfig1.setInt32Encoding(TSEncoding.RLE.name()); + tsFileConfig1.setInt64Encoding(TSEncoding.GORILLA.name()); + tsFileConfig1.setFloatEncoding(TSEncoding.TS_2DIFF.name()); + tsFileConfig1.setDoubleEncoding(TSEncoding.RLE.name()); + tsFileConfig1.setTextEncoding(TSEncoding.DICTIONARY.name()); + + Assert.assertEquals(TSEncoding.PLAIN, tsFileConfig1.getValueEncoder(TSDataType.BOOLEAN)); + Assert.assertEquals(TSEncoding.RLE, tsFileConfig1.getValueEncoder(TSDataType.INT32)); + Assert.assertEquals(TSEncoding.RLE, tsFileConfig1.getValueEncoder(TSDataType.DATE)); + Assert.assertEquals(TSEncoding.GORILLA, tsFileConfig1.getValueEncoder(TSDataType.INT64)); + Assert.assertEquals(TSEncoding.GORILLA, tsFileConfig1.getValueEncoder(TSDataType.TIMESTAMP)); + Assert.assertEquals(TSEncoding.TS_2DIFF, tsFileConfig1.getValueEncoder(TSDataType.FLOAT)); + Assert.assertEquals(TSEncoding.RLE, tsFileConfig1.getValueEncoder(TSDataType.DOUBLE)); + Assert.assertEquals(TSEncoding.DICTIONARY, tsFileConfig1.getValueEncoder(TSDataType.TEXT)); + Assert.assertEquals(TSEncoding.DICTIONARY, tsFileConfig1.getValueEncoder(TSDataType.STRING)); + Assert.assertEquals(TSEncoding.DICTIONARY, tsFileConfig1.getValueEncoder(TSDataType.BLOB)); + Assert.assertEquals(TSEncoding.DICTIONARY, tsFileConfig1.getValueEncoder(TSDataType.OBJECT)); + } + + @Test + public void testGetCompressor() { + tsFileConfig1.setCompressor(CompressionType.ZSTD.name()); + Assert.assertEquals(CompressionType.ZSTD, tsFileConfig1.getCompressor(TSDataType.BOOLEAN)); + Assert.assertEquals(CompressionType.ZSTD, tsFileConfig1.getCompressor(TSDataType.INT32)); + Assert.assertEquals(CompressionType.ZSTD, tsFileConfig1.getCompressor(TSDataType.TEXT)); + + tsFileConfig1.setBooleanCompression(CompressionType.UNCOMPRESSED.name()); + tsFileConfig1.setInt32Compression(CompressionType.SNAPPY.name()); + tsFileConfig1.setInt64Compression(CompressionType.GZIP.name()); + tsFileConfig1.setFloatCompression(CompressionType.LZ4.name()); + tsFileConfig1.setDoubleCompression(CompressionType.ZSTD.name()); + tsFileConfig1.setTextCompression(CompressionType.LZMA2.name()); + + Assert.assertEquals( + CompressionType.UNCOMPRESSED, tsFileConfig1.getCompressor(TSDataType.BOOLEAN)); + Assert.assertEquals(CompressionType.SNAPPY, tsFileConfig1.getCompressor(TSDataType.INT32)); + Assert.assertEquals(CompressionType.SNAPPY, tsFileConfig1.getCompressor(TSDataType.DATE)); + Assert.assertEquals(CompressionType.GZIP, tsFileConfig1.getCompressor(TSDataType.INT64)); + Assert.assertEquals(CompressionType.GZIP, tsFileConfig1.getCompressor(TSDataType.TIMESTAMP)); + Assert.assertEquals(CompressionType.LZ4, tsFileConfig1.getCompressor(TSDataType.FLOAT)); + Assert.assertEquals(CompressionType.ZSTD, tsFileConfig1.getCompressor(TSDataType.DOUBLE)); + Assert.assertEquals(CompressionType.LZMA2, tsFileConfig1.getCompressor(TSDataType.TEXT)); + Assert.assertEquals(CompressionType.LZMA2, tsFileConfig1.getCompressor(TSDataType.STRING)); + Assert.assertEquals(CompressionType.LZMA2, tsFileConfig1.getCompressor(TSDataType.BLOB)); + Assert.assertEquals(CompressionType.LZMA2, tsFileConfig1.getCompressor(TSDataType.OBJECT)); + + tsFileConfig1.setInt32Compression(null); + Assert.assertEquals(CompressionType.ZSTD, tsFileConfig1.getCompressor(TSDataType.INT32)); + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/encoding/decoder/DecoderTest.java b/java/tsfile/src/test/java/org/apache/tsfile/encoding/decoder/DecoderTest.java new file mode 100644 index 000000000..000e08206 --- /dev/null +++ b/java/tsfile/src/test/java/org/apache/tsfile/encoding/decoder/DecoderTest.java @@ -0,0 +1,107 @@ +/* + * 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.tsfile.encoding.decoder; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.exception.encoding.TsFileDecodingException; +import org.apache.tsfile.file.metadata.enums.TSEncoding; + +import org.junit.Assert; +import org.junit.Test; + +public class DecoderTest { + + @Test + public void testGetDecoderByType() { + Object[][] testCases = { + {TSEncoding.PLAIN, TSDataType.UNKNOWN, PlainDecoder.class}, + {TSEncoding.DICTIONARY, TSDataType.BLOB, DictionaryDecoder.class}, + {TSEncoding.RLE, TSDataType.BOOLEAN, IntRleDecoder.class}, + {TSEncoding.RLE, TSDataType.INT32, IntRleDecoder.class}, + {TSEncoding.RLE, TSDataType.DATE, IntRleDecoder.class}, + {TSEncoding.RLE, TSDataType.INT64, LongRleDecoder.class}, + {TSEncoding.RLE, TSDataType.VECTOR, LongRleDecoder.class}, + {TSEncoding.RLE, TSDataType.TIMESTAMP, LongRleDecoder.class}, + {TSEncoding.RLE, TSDataType.FLOAT, FloatDecoder.class}, + {TSEncoding.RLE, TSDataType.DOUBLE, FloatDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.INT32, DeltaBinaryDecoder.IntDeltaDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.DATE, DeltaBinaryDecoder.IntDeltaDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.INT64, DeltaBinaryDecoder.LongDeltaDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.VECTOR, DeltaBinaryDecoder.LongDeltaDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.TIMESTAMP, DeltaBinaryDecoder.LongDeltaDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.FLOAT, FloatDecoder.class}, + {TSEncoding.TS_2DIFF, TSDataType.DOUBLE, FloatDecoder.class}, + {TSEncoding.GORILLA_V1, TSDataType.FLOAT, SinglePrecisionDecoderV1.class}, + {TSEncoding.GORILLA_V1, TSDataType.DOUBLE, DoublePrecisionDecoderV1.class}, + {TSEncoding.REGULAR, TSDataType.INT32, RegularDataDecoder.IntRegularDecoder.class}, + {TSEncoding.REGULAR, TSDataType.VECTOR, RegularDataDecoder.LongRegularDecoder.class}, + {TSEncoding.GORILLA, TSDataType.INT32, IntGorillaDecoder.class}, + {TSEncoding.GORILLA, TSDataType.VECTOR, LongGorillaDecoder.class}, + {TSEncoding.GORILLA, TSDataType.FLOAT, SinglePrecisionDecoderV2.class}, + {TSEncoding.GORILLA, TSDataType.DOUBLE, DoublePrecisionDecoderV2.class}, + {TSEncoding.ZIGZAG, TSDataType.INT32, IntZigzagDecoder.class}, + {TSEncoding.ZIGZAG, TSDataType.INT64, LongZigzagDecoder.class}, + {TSEncoding.CHIMP, TSDataType.INT32, IntChimpDecoder.class}, + {TSEncoding.CHIMP, TSDataType.VECTOR, LongChimpDecoder.class}, + {TSEncoding.CHIMP, TSDataType.FLOAT, SinglePrecisionChimpDecoder.class}, + {TSEncoding.CHIMP, TSDataType.DOUBLE, DoublePrecisionChimpDecoder.class}, + {TSEncoding.SPRINTZ, TSDataType.INT32, IntSprintzDecoder.class}, + {TSEncoding.SPRINTZ, TSDataType.INT64, LongSprintzDecoder.class}, + {TSEncoding.SPRINTZ, TSDataType.FLOAT, FloatSprintzDecoder.class}, + {TSEncoding.SPRINTZ, TSDataType.DOUBLE, DoubleSprintzDecoder.class}, + {TSEncoding.RLBE, TSDataType.INT32, IntRLBEDecoder.class}, + {TSEncoding.RLBE, TSDataType.INT64, LongRLBEDecoder.class}, + {TSEncoding.RLBE, TSDataType.FLOAT, FloatRLBEDecoder.class}, + {TSEncoding.RLBE, TSDataType.DOUBLE, DoubleRLBEDecoder.class}, + {TSEncoding.CAMEL, TSDataType.DOUBLE, CamelDecoder.class} + }; + + for (Object[] testCase : testCases) { + Assert.assertEquals( + testCase[2], + Decoder.getDecoderByType((TSEncoding) testCase[0], (TSDataType) testCase[1]).getClass()); + } + } + + @Test + public void testUnsupportedDecoder() { + Object[][] testCases = { + {TSEncoding.RLE, TSDataType.TEXT}, + {TSEncoding.TS_2DIFF, TSDataType.BOOLEAN}, + {TSEncoding.GORILLA_V1, TSDataType.INT32}, + {TSEncoding.REGULAR, TSDataType.FLOAT}, + {TSEncoding.GORILLA, TSDataType.BOOLEAN}, + {TSEncoding.ZIGZAG, TSDataType.VECTOR}, + {TSEncoding.CHIMP, TSDataType.TEXT}, + {TSEncoding.SPRINTZ, TSDataType.VECTOR}, + {TSEncoding.RLBE, TSDataType.VECTOR}, + {TSEncoding.CAMEL, TSDataType.FLOAT} + }; + + for (Object[] testCase : testCases) { + try { + Decoder.getDecoderByType((TSEncoding) testCase[0], (TSDataType) testCase[1]); + Assert.fail("Expected TsFileDecodingException"); + } catch (TsFileDecodingException ignored) { + // Expected. + } + } + } +} diff --git a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java index ba12c6358..47ad21a2d 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java @@ -20,6 +20,8 @@ import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import org.junit.Test; @@ -34,6 +36,131 @@ @SuppressWarnings({"unchecked", "rawtypes"}) public class StatisticsTest { + private static final TypeService CHECK_STATISTICS_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, INT64, FLOAT, DOUBLE, TIMESTAMP, DATE -> + (statistics, min, max, sum) -> { + assertEquals(min, ((Number) statistics.getMinValue()).intValue()); + assertEquals(max, ((Number) statistics.getMaxValue()).intValue()); + assertEquals(min, ((Number) statistics.getFirstValue()).intValue()); + assertEquals(max, ((Number) statistics.getLastValue()).intValue()); + assertEquals(sum, statistics.getSumDoubleValue(), 0.001); + }; + case BOOLEAN -> + (statistics, min, max, sum) -> { + assertEquals(min % 2 == 1, statistics.getFirstValue()); + assertEquals(max % 2 == 1, statistics.getLastValue()); + assertEquals(sum, statistics.getSumDoubleValue(), 0.001); + }; + case TEXT -> + (statistics, min, max, sum) -> { + assertEquals( + new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), + statistics.getFirstValue()); + assertEquals( + new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), + statistics.getLastValue()); + }; + case STRING -> + (statistics, min, max, sum) -> { + assertEquals( + new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), + statistics.getMinValue()); + assertEquals( + new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), + statistics.getMaxValue()); + assertEquals( + new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), + statistics.getFirstValue()); + assertEquals( + new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), + statistics.getLastValue()); + }; + case BLOB, OBJECT -> (statistics, min, max, sum) -> {}; + case ROW, UNKNOWN, VECTOR -> + (statistics, min, max, sum) -> { + throw new IllegalArgumentException(type.getTypeEnum().toString()); + }; + }; + + private static final TypeService GENERATE_STATISTICS_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32 -> + value -> { + IntegerStatistics statistics = new IntegerStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case INT64 -> + value -> { + LongStatistics statistics = new LongStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case FLOAT -> + value -> { + FloatStatistics statistics = new FloatStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case DOUBLE -> + value -> { + DoubleStatistics statistics = new DoubleStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case TEXT -> + value -> { + BinaryStatistics statistics = new BinaryStatistics(); + statistics.initializeStats( + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET), + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET)); + return statistics; + }; + case STRING -> + value -> { + StringStatistics statistics = new StringStatistics(); + statistics.initializeStats( + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET), + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET), + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET), + new Binary(String.valueOf(value), TSFileConfig.STRING_CHARSET)); + return statistics; + }; + case BOOLEAN -> + value -> { + BooleanStatistics statistics = new BooleanStatistics(); + boolean booleanValue = value % 2 == 1; + statistics.initializeStats(booleanValue, booleanValue, booleanValue ? 1 : 0); + return statistics; + }; + case BLOB -> value -> new BlobStatistics(); + case OBJECT -> value -> new ObjectStatistics(); + case DATE -> + value -> { + DateStatistics statistics = new DateStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case TIMESTAMP -> + value -> { + TimestampStatistics statistics = new TimestampStatistics(); + statistics.initializeStats(value, value, value, value, value); + return statistics; + }; + case ROW, UNKNOWN, VECTOR -> + value -> { + throw new IllegalArgumentException(type.getTypeEnum().toString()); + }; + }; + + static { + CHECK_STATISTICS_SERVICE.check(); + GENERATE_STATISTICS_SERVICE.check(); + } + @Test public void testCrossTypeMerge() { Set dataTypes = new HashSet<>(); @@ -69,120 +196,29 @@ public void testCrossTypeMerge() { private static void checkStatistics(Statistics statistics, int min, int max, double sum) { assertEquals(min, statistics.getStartTime()); assertEquals(max, statistics.getEndTime()); - switch (statistics.getType()) { - case INT32: - case INT64: - case FLOAT: - case DOUBLE: - case TIMESTAMP: - case DATE: - assertEquals(min, ((Number) statistics.getMinValue()).intValue()); - assertEquals(max, ((Number) statistics.getMaxValue()).intValue()); - assertEquals(min, ((Number) statistics.getFirstValue()).intValue()); - assertEquals(max, ((Number) statistics.getLastValue()).intValue()); - assertEquals(sum, statistics.getSumDoubleValue(), 0.001); - break; - case BOOLEAN: - assertEquals(min % 2 == 1, statistics.getFirstValue()); - assertEquals(max % 2 == 1, statistics.getLastValue()); - assertEquals(sum, statistics.getSumDoubleValue(), 0.001); - break; - case TEXT: - assertEquals( - new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), - statistics.getFirstValue()); - assertEquals( - new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), - statistics.getLastValue()); - break; - case STRING: - assertEquals( - new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), statistics.getMinValue()); - assertEquals( - new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), statistics.getMaxValue()); - assertEquals( - new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), - statistics.getFirstValue()); - assertEquals( - new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), - statistics.getLastValue()); - break; - case BLOB: - case OBJECT: - break; - default: - throw new IllegalArgumentException(statistics.getType().toString()); - } + CHECK_STATISTICS_SERVICE + .call(Type.fromTsDataType(statistics.getType())) + .check(statistics, min, max, sum); } private static Statistics genStatistics(TSDataType dataType, int val) { - Statistics result; - switch (dataType) { - case INT32: - IntegerStatistics intStat = new IntegerStatistics(); - intStat.initializeStats(val, val, val, val, val); - result = intStat; - break; - case INT64: - LongStatistics longStat = new LongStatistics(); - longStat.initializeStats(val, val, val, val, val); - result = longStat; - break; - case FLOAT: - FloatStatistics floatStat = new FloatStatistics(); - floatStat.initializeStats(val, val, val, val, val); - result = floatStat; - break; - case DOUBLE: - DoubleStatistics doubleStat = new DoubleStatistics(); - doubleStat.initializeStats(val, val, val, val, val); - result = doubleStat; - break; - case TEXT: - BinaryStatistics binaryStat = new BinaryStatistics(); - binaryStat.initializeStats( - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET), - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET)); - result = binaryStat; - break; - case STRING: - StringStatistics stringStat = new StringStatistics(); - stringStat.initializeStats( - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET), - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET), - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET), - new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET)); - result = stringStat; - break; - case BOOLEAN: - BooleanStatistics boolStat = new BooleanStatistics(); - boolStat.initializeStats(val % 2 == 1, val % 2 == 1, val % 2 == 1 ? 1 : 0); - result = boolStat; - break; - case BLOB: - BlobStatistics blobStat = new BlobStatistics(); - result = blobStat; - break; - case OBJECT: - ObjectStatistics objectStat = new ObjectStatistics(); - result = objectStat; - break; - case DATE: - DateStatistics dateStat = new DateStatistics(); - dateStat.initializeStats(val, val, val, val, val); - result = dateStat; - break; - case TIMESTAMP: - TimestampStatistics timestampStat = new TimestampStatistics(); - timestampStat.initializeStats(val, val, val, val, val); - result = timestampStat; - break; - default: - throw new IllegalArgumentException(dataType.toString()); - } + Statistics result = + GENERATE_STATISTICS_SERVICE.call(Type.fromTsDataType(dataType)).generate(val); result.setStartTime(val); result.setEndTime(val); result.setEmpty(false); return result; } + + @FunctionalInterface + private interface StatisticsChecker { + + void check(Statistics statistics, int min, int max, double sum); + } + + @FunctionalInterface + private interface StatisticsGenerator { + + Statistics generate(int value); + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/common/BatchDataTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/common/BatchDataTest.java index e388fa51e..fde2d0b9c 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/common/BatchDataTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/common/BatchDataTest.java @@ -21,18 +21,58 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.TimeValuePair; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.read.reader.IPointReader; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.TsPrimitiveType; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class BatchDataTest { + private static final TypeService ASSERT_SERIALIZED_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> + (inputStream, expectedValue) -> + assertEquals(expectedValue, inputStream.readBoolean()); + case INT32, DATE -> + (inputStream, expectedValue) -> assertEquals(expectedValue, inputStream.readInt()); + case INT64, TIMESTAMP -> + (inputStream, expectedValue) -> assertEquals(expectedValue, inputStream.readLong()); + case FLOAT -> + (inputStream, expectedValue) -> + assertEquals((float) expectedValue, inputStream.readFloat(), 0); + case DOUBLE -> + (inputStream, expectedValue) -> + assertEquals((double) expectedValue, inputStream.readDouble(), 0); + case TEXT, STRING, BLOB, OBJECT -> + (inputStream, expectedValue) -> { + Binary binary = (Binary) expectedValue; + assertArrayEquals( + binary.getValues(), inputStream.readNBytes(inputStream.readInt())); + }; + case ROW, UNKNOWN, VECTOR -> + (inputStream, expectedValue) -> fail("Unexpected data type: " + type.getTypeEnum()); + }; + + static { + ASSERT_SERIALIZED_VALUE_SERVICE.check(); + } + @Test public void testInt() { BatchData batchData = new BatchData(TSDataType.INT32); @@ -81,4 +121,88 @@ public void testSignal() { } fail(); } + + @Test + public void testSerializeData() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 20260714}, + {TSDataType.INT64, 1L}, + {TSDataType.TIMESTAMP, 2L}, + {TSDataType.FLOAT, 1.0F}, + {TSDataType.DOUBLE, 2.0D}, + {TSDataType.TEXT, binary}, + {TSDataType.STRING, binary}, + {TSDataType.BLOB, binary}, + {TSDataType.OBJECT, binary} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + Object value = testCase[1]; + BatchData batchData = new BatchData(dataType); + batchData.putAnObject(100L, value); + + try (DataInputStream inputStream = serialize(batchData)) { + assertEquals(100L, inputStream.readLong()); + assertSerializedValue(inputStream, dataType, value); + assertEquals(-1, inputStream.read()); + } + } + } + + @Test + public void testSerializeVectorData() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + TsPrimitiveType[] values = { + null, + new TsPrimitiveType.TsBoolean(true), + new TsPrimitiveType.TsInt(1), + new TsPrimitiveType.TsInt(20260714, TSDataType.DATE), + new TsPrimitiveType.TsLong(1L), + new TsPrimitiveType.TsFloat(1.0F), + new TsPrimitiveType.TsDouble(2.0D), + new TsPrimitiveType.TsBinary(binary) + }; + BatchData batchData = new BatchData(TSDataType.VECTOR); + batchData.putAnObject(100L, values); + + try (DataInputStream inputStream = serialize(batchData)) { + assertEquals(100L, inputStream.readLong()); + assertEquals(values.length, inputStream.readInt()); + for (TsPrimitiveType value : values) { + if (value == null) { + assertEquals(0, inputStream.readByte()); + continue; + } + assertEquals(1, inputStream.readByte()); + assertEquals(value.getDataType().serialize(), inputStream.readByte()); + assertSerializedValue(inputStream, value.getDataType(), value.getValue()); + } + assertEquals(-1, inputStream.read()); + } + } + + private DataInputStream serialize(BatchData batchData) throws IOException { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + try (DataOutputStream outputStream = new DataOutputStream(byteStream)) { + batchData.serializeData(outputStream); + } + return new DataInputStream(new ByteArrayInputStream(byteStream.toByteArray())); + } + + private void assertSerializedValue( + DataInputStream inputStream, TSDataType dataType, Object expectedValue) throws IOException { + ASSERT_SERIALIZED_VALUE_SERVICE + .call(Type.fromTsDataType(dataType)) + .assertValue(inputStream, expectedValue); + } + + @FunctionalInterface + private interface SerializedValueAsserter { + + void assertValue(DataInputStream inputStream, Object expectedValue) throws IOException; + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/common/ColumnTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/common/ColumnTest.java index f3a784324..51203e0db 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/common/ColumnTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/common/ColumnTest.java @@ -21,6 +21,7 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.column.BinaryColumn; import org.apache.tsfile.read.common.block.column.BinaryColumnBuilder; import org.apache.tsfile.read.common.block.column.BooleanColumn; @@ -38,17 +39,272 @@ import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; import org.apache.tsfile.read.common.block.column.TimeColumn; import org.apache.tsfile.read.common.block.column.TimeColumnBuilder; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BytesUtils; import org.junit.Assert; import org.junit.Test; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Optional; public class ColumnTest { + @Test + public void testConvertTo() { + Column source = + new IntColumn( + 4, Optional.of(new boolean[] {false, true, false, false}), new int[] {0, 1, 2, 3}) + .getRegion(1, 3); + + Column converted = source.convertTo(TSDataType.DOUBLE); + Assert.assertEquals(TSDataType.DOUBLE, converted.getDataType()); + Assert.assertEquals(3, converted.getPositionCount()); + Assert.assertTrue(converted.isNull(0)); + Assert.assertEquals(2.0D, converted.getDouble(1), 0); + Assert.assertEquals(3.0D, converted.getDouble(2), 0); + + Assert.assertSame(source, source.convertTo(TSDataType.INT32)); + Assert.assertThrows(ClassCastException.class, () -> source.convertTo(TSDataType.BOOLEAN)); + Assert.assertThrows(NullPointerException.class, () -> source.convertTo(null)); + } + + @Test + public void testConvertToText() { + Column dateColumn = + new IntColumn( + 3, + Optional.of(new boolean[] {false, true, false}), + new int[] {20260714, 20260715, 20260716}, + TSDataType.DATE); + + Column converted = dateColumn.convertTo(TSDataType.TEXT); + Assert.assertEquals(TSDataType.TEXT, converted.getDataType()); + Assert.assertEquals(new Binary("2026-07-14", StandardCharsets.UTF_8), converted.getBinary(0)); + Assert.assertTrue(converted.isNull(1)); + Assert.assertEquals(new Binary("2026-07-16", StandardCharsets.UTF_8), converted.getBinary(2)); + } + + @Test + public void testConvertEncodedColumns() { + Column dictionary = + DictionaryColumn.create( + 3, new IntColumn(3, Optional.empty(), new int[] {1, 2, 3}), new int[] {2, 0, 2}); + Column convertedDictionary = dictionary.convertTo(TSDataType.DOUBLE); + Assert.assertTrue(convertedDictionary instanceof DictionaryColumn); + Assert.assertEquals(3.0D, convertedDictionary.getDouble(0), 0); + Assert.assertEquals(1.0D, convertedDictionary.getDouble(1), 0); + Assert.assertEquals(3.0D, convertedDictionary.getDouble(2), 0); + + Column runLengthEncoded = + new RunLengthEncodedColumn(new FloatColumn(1, Optional.empty(), new float[] {1.25F}), 4); + Column convertedRle = runLengthEncoded.convertTo(TSDataType.DOUBLE); + Assert.assertTrue(convertedRle instanceof RunLengthEncodedColumn); + Assert.assertEquals(4, convertedRle.getPositionCount()); + Assert.assertEquals(1.25D, convertedRle.getDouble(3), 0); + + Column nullColumn = new NullColumn(3).convertTo(TSDataType.DOUBLE); + Assert.assertEquals(TSDataType.DOUBLE, nullColumn.getDataType()); + Assert.assertEquals(3, nullColumn.getPositionCount()); + Assert.assertTrue(nullColumn.isNull(0)); + Assert.assertTrue(nullColumn.isNull(2)); + } + + @Test + public void testConvertToRejectsIncompatibleEmptyColumns() { + Column emptyDictionary = + DictionaryColumn.create( + 2, new IntColumn(2, Optional.empty(), new int[] {1, 2}), new int[] {0, 1}) + .getRegion(0, 0); + Column emptyRle = + new RunLengthEncodedColumn( + new IntColumn(1, Optional.of(new boolean[] {true}), new int[1]), 0); + + Assert.assertThrows( + ClassCastException.class, + () -> new IntColumn(0, Optional.empty(), new int[0]).convertTo(TSDataType.BOOLEAN)); + Assert.assertThrows( + ClassCastException.class, + () -> + new IntColumn(2, Optional.of(new boolean[] {true, true}), new int[2]) + .convertTo(TSDataType.BOOLEAN)); + Assert.assertThrows( + ClassCastException.class, () -> emptyDictionary.convertTo(TSDataType.BOOLEAN)); + Assert.assertThrows(ClassCastException.class, () -> emptyRle.convertTo(TSDataType.BOOLEAN)); + } + + @Test + public void testConvertToDoesNotReadBoxedValues() { + Column booleanColumn = + new BooleanColumn(1, Optional.empty(), new boolean[] {true}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column intColumn = + new IntColumn(1, Optional.empty(), new int[] {1}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column longColumn = + new LongColumn(1, Optional.empty(), new long[] {2L}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column floatColumn = + new FloatColumn(1, Optional.empty(), new float[] {3.0F}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column doubleColumn = + new DoubleColumn(1, Optional.empty(), new double[] {4.0D}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column binaryColumn = + new BinaryColumn( + 1, Optional.empty(), new Binary[] {new Binary("5", StandardCharsets.UTF_8)}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + Column timeColumn = + new TimeColumn(1, new long[] {6L}) { + @Override + public Object getObject(int position) { + throw new AssertionError("convertTo must use primitive accessors"); + } + }; + + Assert.assertEquals( + new Binary("true", StandardCharsets.UTF_8), + booleanColumn.convertTo(TSDataType.TEXT).getBinary(0)); + Assert.assertEquals(1.0D, intColumn.convertTo(TSDataType.DOUBLE).getDouble(0), 0); + Assert.assertEquals(2.0D, longColumn.convertTo(TSDataType.DOUBLE).getDouble(0), 0); + Assert.assertEquals(3.0D, floatColumn.convertTo(TSDataType.DOUBLE).getDouble(0), 0); + Assert.assertEquals( + new Binary("4.0", StandardCharsets.UTF_8), + doubleColumn.convertTo(TSDataType.TEXT).getBinary(0)); + Assert.assertEquals( + new Binary("5", StandardCharsets.UTF_8), + binaryColumn.convertTo(TSDataType.BLOB).getBinary(0)); + Assert.assertEquals(6.0D, timeColumn.convertTo(TSDataType.DOUBLE).getDouble(0), 0); + } + + @Test + public void testWriteTo() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Column intDictionary = + DictionaryColumn.create( + 2, new IntColumn(3, Optional.empty(), new int[] {7, 8, 9}), new int[] {2, 0}); + Column binaryRle = + new RunLengthEncodedColumn(new BinaryColumn(1, Optional.empty(), new Binary[] {binary}), 3); + + Object[][] testCases = { + {new BooleanColumn(2, Optional.empty(), new boolean[] {false, true}).getRegion(1, 1), 0}, + {new IntColumn(2, Optional.empty(), new int[] {0, 42}).getRegion(1, 1), 0}, + {new LongColumn(2, Optional.empty(), new long[] {0L, 42L}).getRegion(1, 1), 0}, + {new FloatColumn(2, Optional.empty(), new float[] {0.0F, 1.25F}).getRegion(1, 1), 0}, + {new DoubleColumn(2, Optional.empty(), new double[] {0.0D, 2.5D}).getRegion(1, 1), 0}, + { + new BinaryColumn(2, Optional.empty(), new Binary[] {Binary.EMPTY_VALUE, binary}) + .getRegion(1, 1), + 0 + }, + {new TimeColumn(2, new long[] {0L, 42L}).getRegion(1, 1), 0}, + {intDictionary, 0}, + {binaryRle, 2} + }; + + for (Object[] testCase : testCases) { + Column column = (Column) testCase[0]; + int index = (int) testCase[1]; + ByteArrayOutputStream expectedOutput = new ByteArrayOutputStream(); + Type.fromTsDataType(column.getDataType()) + .serializeValue(column.getObject(index), new DataOutputStream(expectedOutput)); + byte[] expected = expectedOutput.toByteArray(); + + ByteBuffer buffer = ByteBuffer.allocate(expected.length); + column.writeTo(index, buffer); + Assert.assertEquals(expected.length, buffer.position()); + Assert.assertArrayEquals(expected, Arrays.copyOf(buffer.array(), buffer.position())); + + ByteArrayOutputStream actualOutput = new ByteArrayOutputStream(); + column.writeTo(index, new DataOutputStream(actualOutput)); + Assert.assertArrayEquals(expected, actualOutput.toByteArray()); + } + + Column nullColumn = new NullColumn(1); + ByteBuffer emptyBuffer = ByteBuffer.allocate(0); + nullColumn.writeTo(0, emptyBuffer); + ByteArrayOutputStream emptyOutput = new ByteArrayOutputStream(); + nullColumn.writeTo(0, new DataOutputStream(emptyOutput)); + Assert.assertEquals(0, emptyBuffer.position()); + Assert.assertEquals(0, emptyOutput.size()); + } + + @Test + public void testArePositionsEqual() { + IntColumnBuilder builder = new IntColumnBuilder(null, 5); + builder.writeInt(1); + builder.writeInt(1); + builder.writeInt(2); + builder.appendNull(); + builder.appendNull(); + Column column = builder.build(); + + Assert.assertTrue(column.arePositionsEqual(0, 1)); + Assert.assertFalse(column.arePositionsEqual(0, 2)); + Assert.assertFalse(column.arePositionsEqual(0, 3)); + Assert.assertTrue(column.arePositionsEqual(3, 4)); + + IntColumnBuilder thatBuilder = new IntColumnBuilder(null, 3); + thatBuilder.writeInt(1); + thatBuilder.writeInt(2); + thatBuilder.appendNull(); + Column that = thatBuilder.build(); + Assert.assertTrue(column.arePositionsEqual(0, that, 0)); + Assert.assertFalse(column.arePositionsEqual(0, that, 1)); + Assert.assertFalse(column.arePositionsEqual(0, that, 2)); + Assert.assertTrue(column.arePositionsEqual(3, that, 2)); + + Column dictionaryColumn = column.getPositions(new int[] {0, 2, 1}, 0, 3); + Assert.assertTrue(dictionaryColumn.arePositionsEqual(0, 2)); + Assert.assertFalse(dictionaryColumn.arePositionsEqual(0, 1)); + Assert.assertTrue(dictionaryColumn.arePositionsEqual(0, that, 0)); + Assert.assertTrue(that.arePositionsEqual(0, dictionaryColumn, 2)); + + Column runLengthEncodedColumn = new RunLengthEncodedColumn(column.getRegion(0, 1), 3); + Assert.assertTrue(runLengthEncodedColumn.arePositionsEqual(0, 2)); + Assert.assertTrue(runLengthEncodedColumn.arePositionsEqual(0, that, 0)); + Assert.assertTrue(that.arePositionsEqual(0, runLengthEncodedColumn, 2)); + + Column nanColumn = new DoubleColumn(1, Optional.empty(), new double[] {Double.NaN}); + Column nanRunLengthEncodedColumn = new RunLengthEncodedColumn(nanColumn, 2); + Assert.assertFalse(nanRunLengthEncodedColumn.arePositionsEqual(0, 1)); + + Column nullColumn = new NullColumn(2); + Assert.assertTrue(nullColumn.arePositionsEqual(0, 1)); + Assert.assertTrue(nullColumn.arePositionsEqual(0, that, 2)); + Assert.assertFalse(nullColumn.arePositionsEqual(0, that, 0)); + } + @Test public void timeColumnSubColumnTest() { TimeColumnBuilder columnBuilder = new TimeColumnBuilder(null, 10); diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/common/FieldTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/common/FieldTest.java index 1be38da0e..0ee21c4fd 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/common/FieldTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/common/FieldTest.java @@ -18,10 +18,17 @@ */ package org.apache.tsfile.read.common; +import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.NullFieldException; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.junit.Assert; import org.junit.Test; +import java.nio.charset.StandardCharsets; + public class FieldTest { @Test(expected = NullFieldException.class) @@ -29,4 +36,29 @@ public void construct() { Field field = new Field(null); field.getIntV(); } + + @Test + public void copy() { + Object[][] values = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 20260716}, + {TSDataType.INT64, 2L}, + {TSDataType.TIMESTAMP, 3L}, + {TSDataType.FLOAT, 1.25F}, + {TSDataType.DOUBLE, 2.5D}, + {TSDataType.TEXT, new Binary("text", StandardCharsets.UTF_8)}, + {TSDataType.STRING, new Binary("string", StandardCharsets.UTF_8)}, + {TSDataType.BLOB, new Binary(new byte[] {1, 2})}, + {TSDataType.OBJECT, new Binary(BytesUtils.longToBytes(2L))} + }; + + for (Object[] value : values) { + TSDataType dataType = (TSDataType) value[0]; + Field source = Type.fromTsDataType(dataType).getField(value[1]); + Field copy = Field.copy(source); + Assert.assertEquals(source.getDataType(), copy.getDataType()); + Assert.assertEquals(source.getObjectValue(dataType), copy.getObjectValue(dataType)); + } + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/common/type/TypeTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/common/type/TypeTest.java new file mode 100644 index 000000000..6f36ec3a9 --- /dev/null +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/common/type/TypeTest.java @@ -0,0 +1,1946 @@ +/* + * 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.tsfile.read.common.type; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.encoding.encoder.PlainEncoder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.exception.write.UnknownColumnTypeException; +import org.apache.tsfile.file.metadata.IDeviceID; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.BatchData; +import org.apache.tsfile.read.common.Field; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.query.dataset.ResultSet; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.BytesUtils; +import org.apache.tsfile.utils.DateUtils; +import org.apache.tsfile.utils.RamUsageEstimator; +import org.apache.tsfile.utils.TsPrimitiveType; +import org.apache.tsfile.write.UnSupportedDataTypeException; +import org.apache.tsfile.write.chunk.ChunkWriterImpl; +import org.apache.tsfile.write.chunk.ValueChunkWriter; +import org.apache.tsfile.write.record.TSRecord; +import org.apache.tsfile.write.record.datapoint.DataPoint; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.util.Arrays; + +public class TypeTest { + + private static final int OFFSET = 2; + private static final int VALUE_LENGTH = 10; + + @Test + public void testWriteTsPrimitiveTypeToColumnBuilder() { + ColumnBuilder builder = Mockito.mock(ColumnBuilder.class); + + Object[][] testCases = { + {TSDataType.BOOLEAN, new TsPrimitiveType.TsBoolean(true)}, + {TSDataType.INT32, new TsPrimitiveType.TsInt(1)}, + {TSDataType.DATE, new TsPrimitiveType.TsInt(20260721, TSDataType.DATE)}, + {TSDataType.INT64, new TsPrimitiveType.TsLong(2L)}, + {TSDataType.TIMESTAMP, new TsPrimitiveType.TsLong(3L)}, + {TSDataType.FLOAT, new TsPrimitiveType.TsFloat(1.25F)}, + {TSDataType.DOUBLE, new TsPrimitiveType.TsDouble(2.5D)} + }; + for (Object[] testCase : testCases) { + Type.fromTsDataType((TSDataType) testCase[0]).write(builder, (TsPrimitiveType) testCase[1]); + } + + Mockito.verify(builder).writeBoolean(true); + Mockito.verify(builder).writeInt(1); + Mockito.verify(builder).writeInt(20260721); + Mockito.verify(builder).writeLong(2L); + Mockito.verify(builder).writeLong(3L); + Mockito.verify(builder).writeFloat(1.25F); + Mockito.verify(builder).writeDouble(2.5D); + Mockito.reset(builder); + + Binary binary = new Binary("value", StandardCharsets.UTF_8); + TsPrimitiveType binaryValue = new TsPrimitiveType.TsBinary(binary); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(builder, binaryValue); + Mockito.verify(builder).writeBinary(binary); + Mockito.reset(builder); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(builder, new TsPrimitiveType.TsLong(1L)); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteBytesToColumnBuilder() { + ColumnBuilder builder = Mockito.mock(ColumnBuilder.class); + Binary binary = new Binary("value", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, new TsPrimitiveType.TsBoolean(true)}, + {TSDataType.INT32, new TsPrimitiveType.TsInt(1)}, + {TSDataType.DATE, new TsPrimitiveType.TsInt(20260721, TSDataType.DATE)}, + {TSDataType.INT64, new TsPrimitiveType.TsLong(2L)}, + {TSDataType.TIMESTAMP, new TsPrimitiveType.TsLong(3L)}, + {TSDataType.FLOAT, new TsPrimitiveType.TsFloat(1.25F)}, + {TSDataType.DOUBLE, new TsPrimitiveType.TsDouble(2.5D)}, + {TSDataType.TEXT, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.STRING, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.BLOB, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.OBJECT, new TsPrimitiveType.TsBinary(binary)} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + TsPrimitiveType value = (TsPrimitiveType) testCase[1]; + byte[] bytes = new byte[OFFSET + type.calcTypeSize(value)]; + type.toBytes(value, bytes, OFFSET); + type.write(builder, bytes, OFFSET); + } + + Mockito.verify(builder).writeBoolean(true); + Mockito.verify(builder).writeInt(1); + Mockito.verify(builder).writeInt(20260721); + Mockito.verify(builder).writeLong(2L); + Mockito.verify(builder).writeLong(3L); + Mockito.verify(builder).writeFloat(1.25F); + Mockito.verify(builder).writeDouble(2.5D); + Mockito.verify(builder, Mockito.times(4)).writeBinary(binary); + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(builder, new byte[Long.BYTES], 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteColumnValueToColumnBuilder() { + ColumnBuilder builder = Mockito.mock(ColumnBuilder.class); + Column column = Mockito.mock(Column.class); + Binary binary = new Binary("value", StandardCharsets.UTF_8); + + Mockito.when(column.getBoolean(0)).thenReturn(true); + Mockito.when(column.getInt(1)).thenReturn(1); + Mockito.when(column.getInt(2)).thenReturn(20260729); + Mockito.when(column.getLong(3)).thenReturn(2L); + Mockito.when(column.getLong(4)).thenReturn(3L); + Mockito.when(column.getFloat(5)).thenReturn(1.25F); + Mockito.when(column.getDouble(6)).thenReturn(2.5D); + Mockito.when(column.getBinary(7)).thenReturn(binary); + + Object[][] testCases = { + {TSDataType.BOOLEAN, 0}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 2}, + {TSDataType.INT64, 3}, + {TSDataType.TIMESTAMP, 4}, + {TSDataType.FLOAT, 5}, + {TSDataType.DOUBLE, 6} + }; + for (Object[] testCase : testCases) { + Type.fromTsDataType((TSDataType) testCase[0]).write(builder, column, (int) testCase[1]); + } + + Mockito.verify(builder).writeBoolean(true); + Mockito.verify(builder).writeInt(1); + Mockito.verify(builder).writeInt(20260729); + Mockito.verify(builder).writeLong(2L); + Mockito.verify(builder).writeLong(3L); + Mockito.verify(builder).writeFloat(1.25F); + Mockito.verify(builder).writeDouble(2.5D); + Mockito.reset(builder); + + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(builder, column, 7); + Mockito.verify(builder).writeBinary(binary); + Mockito.reset(builder); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(builder, column, 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteTsPrimitiveTypeToArray() { + Binary binary = new Binary("value", StandardCharsets.UTF_8); + TsPrimitiveType[] vector = {new TsPrimitiveType.TsInt(1), new TsPrimitiveType.TsLong(2L)}; + Object[][] testCases = { + {TSDataType.BOOLEAN, new TsPrimitiveType.TsBoolean(true), new boolean[2]}, + {TSDataType.INT32, new TsPrimitiveType.TsInt(1), new int[2]}, + {TSDataType.DATE, new TsPrimitiveType.TsInt(20260723, TSDataType.DATE), new int[2]}, + {TSDataType.DATE, new TsPrimitiveType.TsInt(20260723, TSDataType.DATE), new LocalDate[2]}, + {TSDataType.INT64, new TsPrimitiveType.TsLong(2L), new long[2]}, + {TSDataType.TIMESTAMP, new TsPrimitiveType.TsLong(3L), new long[2]}, + {TSDataType.FLOAT, new TsPrimitiveType.TsFloat(1.25F), new float[2]}, + {TSDataType.DOUBLE, new TsPrimitiveType.TsDouble(2.5D), new double[2]}, + {TSDataType.TEXT, new TsPrimitiveType.TsBinary(binary), new Binary[2]}, + {TSDataType.STRING, new TsPrimitiveType.TsBinary(binary), new Binary[2]}, + {TSDataType.BLOB, new TsPrimitiveType.TsBinary(binary), new Binary[2]}, + {TSDataType.OBJECT, new TsPrimitiveType.TsBinary(binary), new Binary[2]}, + {TSDataType.VECTOR, new TsPrimitiveType.TsVector(vector), new TsPrimitiveType[2][]} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + TsPrimitiveType expected = (TsPrimitiveType) testCase[1]; + Object array = testCase[2]; + type.write(expected, array, 1); + TsPrimitiveType actual = type.getValueAsTsPrimitiveType(array, 1); + Assert.assertEquals(expected, actual); + Assert.assertEquals(expected.getDataType(), actual.getDataType()); + } + + Type rowType = RowType.anonymousRow(IntType.getInstance(), LongType.getInstance()); + TsPrimitiveType rowValue = new TsPrimitiveType.TsVector(vector); + TsPrimitiveType[][] rowArray = new TsPrimitiveType[2][]; + rowType.write(rowValue, rowArray, 1); + Assert.assertEquals(rowValue, rowType.getValueAsTsPrimitiveType(rowArray, 1)); + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).write(new TsPrimitiveType.TsInt(1), new int[1], 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testWriteBatchDataToArray() { + Binary binary = new Binary("value", StandardCharsets.UTF_8); + TsPrimitiveType[] vector = {new TsPrimitiveType.TsInt(1), new TsPrimitiveType.TsLong(2L)}; + Object[][] testCases = { + {TSDataType.BOOLEAN, true, new boolean[2]}, + {TSDataType.INT32, 1, new int[2]}, + {TSDataType.DATE, 20260723, new int[2]}, + {TSDataType.DATE, 20260723, new LocalDate[2]}, + {TSDataType.INT64, 2L, new long[2]}, + {TSDataType.TIMESTAMP, 3L, new long[2]}, + {TSDataType.FLOAT, 1.25F, new float[2]}, + {TSDataType.DOUBLE, 2.5D, new double[2]}, + {TSDataType.TEXT, binary, new Binary[2]}, + {TSDataType.STRING, binary, new Binary[2]}, + {TSDataType.BLOB, binary, new Binary[2]}, + {TSDataType.OBJECT, binary, new Binary[2]}, + {TSDataType.VECTOR, vector, new TsPrimitiveType[2][]} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + Type type = Type.fromTsDataType(dataType); + BatchData batchData = new BatchData(dataType); + batchData.putAnObject(1L, testCase[1]); + Object array = testCase[2]; + type.write(batchData, array, 1); + TsPrimitiveType actual = type.getValueAsTsPrimitiveType(array, 1); + Assert.assertEquals(batchData.currentTsPrimitiveType(), actual); + Assert.assertEquals(batchData.currentTsPrimitiveType().getDataType(), actual.getDataType()); + } + + Type rowType = RowType.anonymousRow(IntType.getInstance(), LongType.getInstance()); + BatchData vectorBatchData = new BatchData(TSDataType.VECTOR); + vectorBatchData.putAnObject(1L, vector); + TsPrimitiveType[][] rowArray = new TsPrimitiveType[2][]; + rowType.write(vectorBatchData, rowArray, 1); + Assert.assertEquals( + vectorBatchData.currentTsPrimitiveType(), rowType.getValueAsTsPrimitiveType(rowArray, 1)); + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).write((BatchData) null, new Object[1], 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testGetValueAsTsPrimitiveType() { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + TsPrimitiveType[] vector = {new TsPrimitiveType.TsInt(1), null}; + Object[][] testCases = { + {TSDataType.BOOLEAN, new boolean[] {false, true}, new TsPrimitiveType.TsBoolean(true)}, + {TSDataType.INT32, new int[] {0, 1}, new TsPrimitiveType.TsInt(1)}, + { + TSDataType.DATE, + new int[] {0, 20260717}, + new TsPrimitiveType.TsInt(20260717, TSDataType.DATE) + }, + { + TSDataType.DATE, + new LocalDate[] {null, LocalDate.of(2026, 7, 17)}, + new TsPrimitiveType.TsInt(20260717, TSDataType.DATE) + }, + {TSDataType.INT64, new long[] {0L, 1L}, new TsPrimitiveType.TsLong(1L)}, + {TSDataType.TIMESTAMP, new long[] {0L, 1L}, new TsPrimitiveType.TsLong(1L)}, + {TSDataType.FLOAT, new float[] {0.0F, 1.25F}, new TsPrimitiveType.TsFloat(1.25F)}, + {TSDataType.DOUBLE, new double[] {0.0D, 2.5D}, new TsPrimitiveType.TsDouble(2.5D)}, + {TSDataType.TEXT, new Binary[] {null, binary}, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.STRING, new Binary[] {null, binary}, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.BLOB, new Binary[] {null, binary}, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.OBJECT, new Binary[] {null, binary}, new TsPrimitiveType.TsBinary(binary)}, + { + TSDataType.VECTOR, + new TsPrimitiveType[][] {null, vector}, + new TsPrimitiveType.TsVector(vector) + } + }; + + for (Object[] testCase : testCases) { + TsPrimitiveType expected = (TsPrimitiveType) testCase[2]; + TsPrimitiveType actual = + Type.fromTsDataType((TSDataType) testCase[0]).getValueAsTsPrimitiveType(testCase[1], 1); + Assert.assertEquals(expected, actual); + Assert.assertEquals(expected.getDataType(), actual.getDataType()); + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN) + .getValueAsTsPrimitiveType(new Object[] {new Object()}, 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testToBytes() { + byte[] valueBytes = new byte[16]; + + Type.fromTsDataType(TSDataType.BOOLEAN) + .toBytes(new TsPrimitiveType.TsBoolean(true), valueBytes, OFFSET); + Assert.assertTrue(BytesUtils.bytesToBool(valueBytes, OFFSET)); + + Type.fromTsDataType(TSDataType.DATE) + .toBytes(new TsPrimitiveType.TsInt(20260713, TSDataType.DATE), valueBytes, OFFSET); + Assert.assertEquals(20260713, BytesUtils.bytesToInt(valueBytes, OFFSET)); + + Type.fromTsDataType(TSDataType.TIMESTAMP) + .toBytes(new TsPrimitiveType.TsLong(123456789L), valueBytes, OFFSET); + Assert.assertEquals( + 123456789L, BytesUtils.bytesToLongFromOffset(valueBytes, Long.BYTES, OFFSET)); + + Type.fromTsDataType(TSDataType.FLOAT) + .toBytes(new TsPrimitiveType.TsFloat(1.25F), valueBytes, OFFSET); + Assert.assertEquals(1.25F, BytesUtils.bytesToFloat(valueBytes, OFFSET), 0); + + Type.fromTsDataType(TSDataType.DOUBLE) + .toBytes(new TsPrimitiveType.TsDouble(2.5D), valueBytes, OFFSET); + Assert.assertEquals(2.5D, BytesUtils.bytesToDouble(valueBytes, OFFSET), 0); + + Binary binary = new Binary("test", StandardCharsets.UTF_8); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType) + .toBytes(new TsPrimitiveType.TsBinary(binary), valueBytes, OFFSET); + Assert.assertEquals(binary.getLength(), BytesUtils.bytesToInt(valueBytes, OFFSET)); + Assert.assertArrayEquals( + binary.getValues(), + Arrays.copyOfRange( + valueBytes, OFFSET + Integer.BYTES, OFFSET + Integer.BYTES + binary.getLength())); + } + } + + @Test + public void testCalcTypeSize() { + Assert.assertEquals( + Byte.BYTES, + Type.fromTsDataType(TSDataType.BOOLEAN).calcTypeSize(new TsPrimitiveType.TsBoolean(true))); + Assert.assertEquals( + Integer.BYTES, + Type.fromTsDataType(TSDataType.INT32).calcTypeSize(new TsPrimitiveType.TsInt(1))); + Assert.assertEquals( + Integer.BYTES, + Type.fromTsDataType(TSDataType.DATE) + .calcTypeSize(new TsPrimitiveType.TsInt(20260713, TSDataType.DATE))); + Assert.assertEquals( + Long.BYTES, + Type.fromTsDataType(TSDataType.INT64).calcTypeSize(new TsPrimitiveType.TsLong(1L))); + Assert.assertEquals( + Long.BYTES, + Type.fromTsDataType(TSDataType.TIMESTAMP).calcTypeSize(new TsPrimitiveType.TsLong(1L))); + Assert.assertEquals( + Float.BYTES, + Type.fromTsDataType(TSDataType.FLOAT).calcTypeSize(new TsPrimitiveType.TsFloat(1.0F))); + Assert.assertEquals( + Double.BYTES, + Type.fromTsDataType(TSDataType.DOUBLE).calcTypeSize(new TsPrimitiveType.TsDouble(1.0D))); + + Binary binary = new Binary("test", StandardCharsets.UTF_8); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Assert.assertEquals( + Integer.BYTES + binary.getLength(), + Type.fromTsDataType(dataType).calcTypeSize(new TsPrimitiveType.TsBinary(binary))); + } + } + + @Test + public void testSerializeValue() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 20260716}, + {TSDataType.INT64, 2L}, + {TSDataType.TIMESTAMP, 3L}, + {TSDataType.FLOAT, 1.25F}, + {TSDataType.DOUBLE, 2.5D}, + {TSDataType.TEXT, binary}, + {TSDataType.STRING, binary}, + {TSDataType.BLOB, binary}, + {TSDataType.OBJECT, binary} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + Object value = testCase[1]; + ByteBuffer buffer = ByteBuffer.allocate(64); + int bufferSerializedSize = type.serializeValue(value, buffer); + Assert.assertEquals(buffer.position(), bufferSerializedSize); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int serializedSize = + type.serialize(type.getTsPrimitiveType(value), new DataOutputStream(output)); + byte[] expected = output.toByteArray(); + Assert.assertEquals(expected.length, serializedSize); + Assert.assertArrayEquals(expected, Arrays.copyOf(buffer.array(), buffer.position())); + + output.reset(); + int streamSerializedSize = type.serializeValue(value, new DataOutputStream(output)); + Assert.assertEquals(output.size(), streamSerializedSize); + Assert.assertArrayEquals(expected, output.toByteArray()); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).serializeValue(null, ByteBuffer.allocate(0)); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + + try { + Type.fromTsDataType(dataType) + .serializeValue(null, new DataOutputStream(new ByteArrayOutputStream())); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testSerializeBatchDataReturnsSerializedSize() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 20260722}, + {TSDataType.INT64, 2L}, + {TSDataType.TIMESTAMP, 3L}, + {TSDataType.FLOAT, 1.25F}, + {TSDataType.DOUBLE, 2.5D}, + {TSDataType.TEXT, binary}, + {TSDataType.STRING, binary}, + {TSDataType.BLOB, binary}, + {TSDataType.OBJECT, binary}, + { + TSDataType.VECTOR, + new TsPrimitiveType[] { + null, new TsPrimitiveType.TsInt(1), new TsPrimitiveType.TsBinary(binary) + } + } + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + BatchData batchData = new BatchData((TSDataType) testCase[0]); + batchData.putAnObject(1L, testCase[1]); + batchData.putAnObject(2L, testCase[1]); + + for (boolean isDesc : new boolean[] {false, true}) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int serializedSize = type.serialize(batchData, new DataOutputStream(output), isDesc); + Assert.assertEquals(output.size(), serializedSize); + } + } + } + + @Test + public void testDeserializeValue() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 20260716}, + {TSDataType.INT64, 2L}, + {TSDataType.TIMESTAMP, 3L}, + {TSDataType.FLOAT, 1.25F}, + {TSDataType.DOUBLE, 2.5D}, + {TSDataType.TEXT, binary}, + {TSDataType.STRING, binary}, + {TSDataType.BLOB, binary}, + {TSDataType.OBJECT, binary} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + type.serializeValue(testCase[1], new DataOutputStream(output)); + Object[] values = new Object[2]; + type.deserialize(values, 1, ByteBuffer.wrap(output.toByteArray())); + Assert.assertEquals(testCase[1], values[1]); + + values[1] = null; + type.deserialize(values, 1, new ByteArrayInputStream(output.toByteArray())); + Assert.assertEquals(testCase[1], values[1]); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).deserialize(new Object[1], 0, ByteBuffer.allocate(0)); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + + try { + Type.fromTsDataType(dataType) + .deserialize(new Object[1], 0, new ByteArrayInputStream(new byte[0])); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testSerializeArrayToByteBuffer() throws IOException { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, new boolean[] {true, false}}, + {TSDataType.INT32, new int[] {1, 2}}, + {TSDataType.DATE, new int[] {20260716, 20260717}}, + {TSDataType.INT64, new long[] {3L, 4L}}, + {TSDataType.TIMESTAMP, new long[] {5L, 6L}}, + {TSDataType.FLOAT, new float[] {1.25F, 2.5F}}, + {TSDataType.DOUBLE, new double[] {2.5D, 5.0D}}, + {TSDataType.TEXT, new Binary[] {binary, null}}, + {TSDataType.STRING, new Binary[] {binary, null}}, + {TSDataType.BLOB, new Binary[] {binary, null}}, + {TSDataType.OBJECT, new Binary[] {binary, null}} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + Object array = testCase[1]; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + type.serializeArray(array, 2, new DataOutputStream(output)); + + ByteBuffer buffer = ByteBuffer.allocate(type.serializedSize(array, 2)); + type.serializeArray(array, 2, buffer); + Assert.assertArrayEquals( + output.toByteArray(), Arrays.copyOf(buffer.array(), buffer.position())); + } + + LocalDate[] dates = {LocalDate.of(2026, 7, 16), null}; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Type dateType = Type.fromTsDataType(TSDataType.DATE); + dateType.serializeArray(dates, dates.length, new DataOutputStream(output)); + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * dates.length); + dateType.serializeArray(dates, dates.length, buffer); + Assert.assertArrayEquals(output.toByteArray(), buffer.array()); + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).serializeArray(new Object[0], 0, ByteBuffer.allocate(0)); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testSerializedSizeRange() { + Assert.assertEquals( + Integer.BYTES * 2, + Type.fromTsDataType(TSDataType.INT32).serializedSize(new int[] {1, 2, 3}, 1, 3)); + + Binary[] binaries = { + new Binary("excluded", StandardCharsets.UTF_8), null, new Binary("x", StandardCharsets.UTF_8) + }; + int expectedSize = Byte.BYTES + Byte.BYTES + Integer.BYTES + 1; + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type type = Type.fromTsDataType(dataType); + Assert.assertEquals(expectedSize, type.serializedSize(binaries, 1, 3)); + Assert.assertEquals(0, type.serializedSize(binaries, 2, 2)); + } + } + + @Test + public void testGetDataPoint() { + Object[][] testCases = { + {TSDataType.BOOLEAN, "true", true}, + {TSDataType.INT32, "1", 1}, + {TSDataType.DATE, "2026-07-15", 20260715}, + {TSDataType.INT64, "2", 2L}, + {TSDataType.TIMESTAMP, "3", 3L}, + {TSDataType.FLOAT, "1.25", 1.25F}, + {TSDataType.DOUBLE, "2.5", 2.5D}, + {TSDataType.TEXT, "text", new Binary("text", TSFileConfig.STRING_CHARSET)}, + {TSDataType.STRING, "string", new Binary("string", TSFileConfig.STRING_CHARSET)}, + {TSDataType.BLOB, "blob", new Binary("blob", TSFileConfig.STRING_CHARSET)}, + {TSDataType.OBJECT, "object", new Binary("object", TSFileConfig.STRING_CHARSET)} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + String value = (String) testCase[1]; + Object expectedValue = testCase[2]; + + DataPoint typeDataPoint = Type.fromTsDataType(dataType).getDataPoint("s", value); + Assert.assertEquals("s", typeDataPoint.getMeasurementId()); + Assert.assertEquals(expectedValue, typeDataPoint.getValue()); + + DataPoint factoryDataPoint = DataPoint.getDataPoint(dataType, "s", value); + Assert.assertEquals(typeDataPoint.getClass(), factoryDataPoint.getClass()); + Assert.assertEquals(expectedValue, factoryDataPoint.getValue()); + } + + try { + DataPoint.getDataPoint(TSDataType.INT32, "s", "invalid"); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + + try { + DataPoint.getDataPoint(TSDataType.VECTOR, "s", "1"); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + @Test + public void testGetDataPointFromLong() { + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 1}, + {TSDataType.INT64, 1L}, + {TSDataType.TIMESTAMP, 1L}, + {TSDataType.FLOAT, 1.0F}, + {TSDataType.DOUBLE, 1.0D}, + {TSDataType.TEXT, new Binary("1", TSFileConfig.STRING_CHARSET)}, + {TSDataType.STRING, new Binary("1", TSFileConfig.STRING_CHARSET)}, + {TSDataType.BLOB, new Binary("1", TSFileConfig.STRING_CHARSET)}, + {TSDataType.OBJECT, new Binary("1", TSFileConfig.STRING_CHARSET)}, + {TSDataType.VECTOR, new Binary("1", TSFileConfig.STRING_CHARSET)}, + {TSDataType.UNKNOWN, new Binary("1", TSFileConfig.STRING_CHARSET)} + }; + + for (Object[] testCase : testCases) { + DataPoint dataPoint = Type.fromTsDataType((TSDataType) testCase[0]).getDataPoint("s", 1L); + Assert.assertEquals("s", dataPoint.getMeasurementId()); + Assert.assertEquals(testCase[1], dataPoint.getValue()); + } + } + + @Test + public void testWriteValueChunk() { + ValueChunkWriter writer = Mockito.mock(ValueChunkWriter.class); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 1L, true, false); + Mockito.verify(writer).write(1L, true, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.INT32).write(writer, 2L, 1, false); + Mockito.verify(writer).write(2L, 1, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DATE).write(writer, 3L, null, true); + Mockito.verify(writer).write(3L, 0, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TIMESTAMP).write(writer, 4L, 2L, false); + Mockito.verify(writer).write(4L, 2L, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 5L, 1.25F, false); + Mockito.verify(writer).write(5L, 1.25F, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 6L, 2.5D, false); + Mockito.verify(writer).write(6L, 2.5D, false); + Mockito.reset(writer); + + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 7L, binary, false); + Mockito.verify(writer).write(7L, binary, false); + Mockito.reset(writer); + } + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 8L, null, true); + Mockito.verify(writer).write(8L, false, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.INT64).write(writer, 9L, null, true); + Mockito.verify(writer).write(9L, 0L, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 10L, null, true); + Mockito.verify(writer).write(10L, 0.0F, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 11L, null, true); + Mockito.verify(writer).write(11L, 0.0D, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TEXT).write(writer, 12L, null, true); + Mockito.verify(writer).write(12L, Binary.EMPTY_VALUE, true); + Mockito.reset(writer); + + try { + Type.fromTsDataType(TSDataType.VECTOR).write(writer, 13L, null, true); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + @Test + public void testWriteTsPrimitiveValueChunk() { + ValueChunkWriter writer = Mockito.mock(ValueChunkWriter.class); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 1L, new TsPrimitiveType.TsBoolean(true)); + Mockito.verify(writer).write(1L, true, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.INT32).write(writer, 2L, new TsPrimitiveType.TsInt(1)); + Mockito.verify(writer).write(2L, 1, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TIMESTAMP).write(writer, 3L, new TsPrimitiveType.TsLong(2L)); + Mockito.verify(writer).write(3L, 2L, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 4L, new TsPrimitiveType.TsFloat(1.25F)); + Mockito.verify(writer).write(4L, 1.25F, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 5L, new TsPrimitiveType.TsDouble(2.5D)); + Mockito.verify(writer).write(5L, 2.5D, false); + Mockito.reset(writer); + + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 6L, new TsPrimitiveType.TsBinary(binary)); + Mockito.verify(writer).write(6L, binary, false); + Mockito.reset(writer); + } + + Type.fromTsDataType(TSDataType.INT32).write(writer, 7L, (TsPrimitiveType) null); + Mockito.verify(writer).write(7L, Integer.MAX_VALUE, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TIMESTAMP).write(writer, 8L, (TsPrimitiveType) null); + Mockito.verify(writer).write(8L, Long.MAX_VALUE, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 9L, (TsPrimitiveType) null); + Mockito.verify(writer).write(9L, Float.MAX_VALUE, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 10L, (TsPrimitiveType) null); + Mockito.verify(writer).write(10L, Double.MAX_VALUE, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 11L, (TsPrimitiveType) null); + Mockito.verify(writer).write(11L, false, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TEXT).write(writer, 12L, (TsPrimitiveType) null); + Mockito.verify(writer).write(12L, Binary.EMPTY_VALUE, true); + Mockito.reset(writer); + + try { + Type.fromTsDataType(TSDataType.VECTOR).write(writer, 13L, (TsPrimitiveType) null); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + @Test + public void testWriteTabletValueChunk() { + ValueChunkWriter writer = Mockito.mock(ValueChunkWriter.class); + + Type.fromTsDataType(TSDataType.BOOLEAN) + .write(writer, 1L, new boolean[] {false, true}, 1, false); + Mockito.verify(writer).write(1L, true, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.INT32).write(writer, 2L, new int[] {0, 1}, 1, false); + Mockito.verify(writer).write(2L, 1, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DATE) + .write( + writer, + 3L, + new LocalDate[] {LocalDate.of(2026, 7, 14), LocalDate.of(2026, 7, 15)}, + 1, + false); + Mockito.verify(writer).write(3L, 20260715, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TIMESTAMP).write(writer, 4L, new long[] {0L, 2L}, 1, false); + Mockito.verify(writer).write(4L, 2L, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 5L, new float[] {0F, 1.25F}, 1, false); + Mockito.verify(writer).write(5L, 1.25F, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 6L, new double[] {0D, 2.5D}, 1, false); + Mockito.verify(writer).write(6L, 2.5D, false); + Mockito.reset(writer); + + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + Binary[] binaries = {Binary.EMPTY_VALUE, binary}; + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 7L, binaries, 1, false); + Mockito.verify(writer).write(7L, binary, false); + Mockito.reset(writer); + } + + try { + Type.fromTsDataType(TSDataType.VECTOR).write(writer, 8L, new long[] {1L}, 0, false); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + @Test + public void testWriteTsBlockValueChunk() { + ValueChunkWriter writer = Mockito.mock(ValueChunkWriter.class); + Column column = Mockito.mock(Column.class); + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + Mockito.when(column.getBoolean(1)).thenReturn(true); + Mockito.when(column.getInt(1)).thenReturn(1); + Mockito.when(column.getLong(1)).thenReturn(2L); + Mockito.when(column.getFloat(1)).thenReturn(1.25F); + Mockito.when(column.getDouble(1)).thenReturn(2.5D); + Mockito.when(column.getBinary(1)).thenReturn(binary); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 1L, column, 1, false); + Mockito.verify(writer).write(1L, true, false); + Mockito.reset(writer); + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT32, TSDataType.DATE}) { + Type.fromTsDataType(dataType).write(writer, 2L, column, 1, false); + Mockito.verify(writer).write(2L, 1, false); + Mockito.reset(writer); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT64, TSDataType.TIMESTAMP}) { + Type.fromTsDataType(dataType).write(writer, 3L, column, 1, false); + Mockito.verify(writer).write(3L, 2L, false); + Mockito.reset(writer); + } + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 4L, column, 1, false); + Mockito.verify(writer).write(4L, 1.25F, false); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 5L, column, 1, false); + Mockito.verify(writer).write(5L, 2.5D, false); + Mockito.reset(writer); + + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB}) { + Type.fromTsDataType(dataType).write(writer, 6L, column, 1, false); + Mockito.verify(writer).write(6L, binary, false); + Mockito.reset(writer); + } + + Type.fromTsDataType(TSDataType.INT32).write(writer, 7L, column, 1, true); + Mockito.verify(writer).write(7L, 0, true); + Mockito.reset(writer); + + for (TSDataType dataType : new TSDataType[] {TSDataType.OBJECT, TSDataType.VECTOR}) { + try { + Type.fromTsDataType(dataType).write(writer, 8L, column, 1, false); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteTsBlockValueChunkBatch() { + ValueChunkWriter writer = Mockito.mock(ValueChunkWriter.class); + Column column = Mockito.mock(Column.class); + long[] times = {1L, 2L}; + boolean[] isNull = {false, true}; + boolean[] booleans = {true, false}; + int[] ints = {1, 0}; + long[] longs = {2L, 0L}; + float[] floats = {1.25F, 0F}; + double[] doubles = {2.5D, 0D}; + Binary[] binaries = {new Binary("value", TSFileConfig.STRING_CHARSET), Binary.EMPTY_VALUE}; + Mockito.when(column.isNull()).thenReturn(isNull); + Mockito.when(column.getBooleans()).thenReturn(booleans); + Mockito.when(column.getInts()).thenReturn(ints); + Mockito.when(column.getLongs()).thenReturn(longs); + Mockito.when(column.getFloats()).thenReturn(floats); + Mockito.when(column.getDoubles()).thenReturn(doubles); + Mockito.when(column.getBinaries()).thenReturn(binaries); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, booleans, isNull, 1, 1); + Mockito.reset(writer); + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT32, TSDataType.DATE}) { + Type.fromTsDataType(dataType).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, ints, isNull, 1, 1); + Mockito.reset(writer); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT64, TSDataType.TIMESTAMP}) { + Type.fromTsDataType(dataType).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, longs, isNull, 1, 1); + Mockito.reset(writer); + } + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, floats, isNull, 1, 1); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, doubles, isNull, 1, 1); + Mockito.reset(writer); + + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, times, column, 1, 1); + Mockito.verify(writer).write(times, binaries, isNull, 1, 1); + Mockito.reset(writer); + } + + try { + Type.fromTsDataType(TSDataType.VECTOR).write(writer, times, column, 1, 1); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testWriteNonAlignedTabletValueChunk() { + ChunkWriterImpl writer = Mockito.mock(ChunkWriterImpl.class); + + Type.fromTsDataType(TSDataType.BOOLEAN).write(writer, 1L, new boolean[] {false, true}, 1); + Mockito.verify(writer).write(1L, true); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.INT32).write(writer, 2L, new int[] {0, 1}, 1); + Mockito.verify(writer).write(2L, 1); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DATE) + .write( + writer, 3L, new LocalDate[] {LocalDate.of(2026, 7, 14), LocalDate.of(2026, 7, 15)}, 1); + Mockito.verify(writer).write(3L, 20260715); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.TIMESTAMP).write(writer, 4L, new long[] {0L, 2L}, 1); + Mockito.verify(writer).write(4L, 2L); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.FLOAT).write(writer, 5L, new float[] {0F, 1.25F}, 1); + Mockito.verify(writer).write(5L, 1.25F); + Mockito.reset(writer); + + Type.fromTsDataType(TSDataType.DOUBLE).write(writer, 6L, new double[] {0D, 2.5D}, 1); + Mockito.verify(writer).write(6L, 2.5D); + Mockito.reset(writer); + + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + Binary[] binaries = {Binary.EMPTY_VALUE, binary}; + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 7L, binaries, 1); + Mockito.verify(writer).write(7L, binary); + Mockito.reset(writer); + } + + try { + Type.fromTsDataType(TSDataType.VECTOR).write(writer, 8L, new long[] {1L}, 0); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + @Test + public void testWriteNonAlignedValueChunk() { + ChunkWriterImpl writer = Mockito.mock(ChunkWriterImpl.class); + + Object[][] testCases = { + {TSDataType.BOOLEAN, 1L, true}, + {TSDataType.INT32, 2L, 1}, + {TSDataType.DATE, 3L, 20260717}, + {TSDataType.INT64, 4L, 2L}, + {TSDataType.TIMESTAMP, 5L, 3L}, + {TSDataType.FLOAT, 6L, 1.25F}, + {TSDataType.DOUBLE, 7L, 2.5D} + }; + for (Object[] testCase : testCases) { + Type.fromTsDataType((TSDataType) testCase[0]).write(writer, (long) testCase[1], testCase[2]); + } + + Mockito.verify(writer).write(1L, true); + Mockito.verify(writer).write(2L, 1); + Mockito.verify(writer).write(3L, 20260717); + Mockito.verify(writer).write(4L, 2L); + Mockito.verify(writer).write(5L, 3L); + Mockito.verify(writer).write(6L, 1.25F); + Mockito.verify(writer).write(7L, 2.5D); + Mockito.reset(writer); + + Binary binary = new Binary("value", StandardCharsets.UTF_8); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 8L, binary); + Mockito.verify(writer).write(8L, binary); + Mockito.reset(writer); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(writer, 9L, 1L); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteTsPrimitiveTypeToNonAlignedValueChunk() { + ChunkWriterImpl writer = Mockito.mock(ChunkWriterImpl.class); + + Object[][] testCases = { + {TSDataType.BOOLEAN, 1L, new TsPrimitiveType.TsBoolean(true)}, + {TSDataType.INT32, 2L, new TsPrimitiveType.TsInt(1)}, + {TSDataType.DATE, 3L, new TsPrimitiveType.TsInt(20260717, TSDataType.DATE)}, + {TSDataType.INT64, 4L, new TsPrimitiveType.TsLong(2L)}, + {TSDataType.TIMESTAMP, 5L, new TsPrimitiveType.TsLong(3L)}, + {TSDataType.FLOAT, 6L, new TsPrimitiveType.TsFloat(1.25F)}, + {TSDataType.DOUBLE, 7L, new TsPrimitiveType.TsDouble(2.5D)} + }; + for (Object[] testCase : testCases) { + Type.fromTsDataType((TSDataType) testCase[0]) + .write(writer, (long) testCase[1], (TsPrimitiveType) testCase[2]); + } + + Mockito.verify(writer).write(1L, true); + Mockito.verify(writer).write(2L, 1); + Mockito.verify(writer).write(3L, 20260717); + Mockito.verify(writer).write(4L, 2L); + Mockito.verify(writer).write(5L, 3L); + Mockito.verify(writer).write(6L, 1.25F); + Mockito.verify(writer).write(7L, 2.5D); + Mockito.reset(writer); + + Binary binary = new Binary("value", StandardCharsets.UTF_8); + TsPrimitiveType binaryValue = new TsPrimitiveType.TsBinary(binary); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Type.fromTsDataType(dataType).write(writer, 8L, binaryValue); + Mockito.verify(writer).write(8L, binary); + Mockito.reset(writer); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(writer, 9L, new TsPrimitiveType.TsLong(1L)); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testWriteBatchDataToNonAlignedValueChunk() { + ChunkWriterImpl writer = Mockito.mock(ChunkWriterImpl.class); + + Object[][] testCases = { + {TSDataType.BOOLEAN, 1L, true}, + {TSDataType.INT32, 2L, 1}, + {TSDataType.DATE, 3L, 20260717}, + {TSDataType.INT64, 4L, 2L}, + {TSDataType.TIMESTAMP, 5L, 3L}, + {TSDataType.FLOAT, 6L, 1.25F}, + {TSDataType.DOUBLE, 7L, 2.5D} + }; + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + BatchData data = new BatchData(dataType); + data.putAnObject(0L, testCase[2]); + Type.fromTsDataType(dataType).write(writer, (long) testCase[1], data); + } + + Mockito.verify(writer).write(1L, true); + Mockito.verify(writer).write(2L, 1); + Mockito.verify(writer).write(3L, 20260717); + Mockito.verify(writer).write(4L, 2L); + Mockito.verify(writer).write(5L, 3L); + Mockito.verify(writer).write(6L, 1.25F); + Mockito.verify(writer).write(7L, 2.5D); + Mockito.reset(writer); + + Binary binary = new Binary("value", StandardCharsets.UTF_8); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + BatchData data = new BatchData(dataType); + data.putAnObject(0L, binary); + Type.fromTsDataType(dataType).write(writer, 8L, data); + Mockito.verify(writer).write(8L, binary); + Mockito.reset(writer); + } + + BatchData vectorData = new BatchData(TSDataType.VECTOR); + vectorData.putAnObject(0L, new TsPrimitiveType[] {new TsPrimitiveType.TsLong(1L)}); + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).write(writer, 9L, vectorData); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testToString() { + Object[][] testCases = { + {TSDataType.BOOLEAN, true, "true"}, + {TSDataType.INT32, 1, "1"}, + {TSDataType.DATE, 20260714, "20260714"}, + {TSDataType.INT64, 2L, "2"}, + {TSDataType.TIMESTAMP, 3L, "3"}, + {TSDataType.FLOAT, 1.25F, "1.25"}, + {TSDataType.DOUBLE, 2.5D, "2.5"}, + {TSDataType.TEXT, new Binary("text", StandardCharsets.UTF_8), "text"}, + {TSDataType.STRING, new Binary("string", StandardCharsets.UTF_8), "string"}, + {TSDataType.BLOB, new Binary(new byte[] {0x01, 0x23, (byte) 0xFF}), "0x0123ff"}, + {TSDataType.OBJECT, new Binary(BytesUtils.longToBytes(1L)), "(Object) 1 B"} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + Field field = Field.getField(testCase[1], dataType); + Assert.assertEquals(testCase[2], Type.fromTsDataType(dataType).toString(field)); + Assert.assertEquals(testCase[2], field.getStringValue()); + } + + Assert.assertEquals("null", new Field(null).getStringValue()); + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + new Field(dataType).getStringValue(); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testGetValueFromField() { + Object[][] testCases = { + {TSDataType.BOOLEAN, true, true}, + {TSDataType.INT32, 1, 1}, + {TSDataType.DATE, 20260714, LocalDate.of(2026, 7, 14)}, + {TSDataType.INT64, 2L, 2L}, + {TSDataType.TIMESTAMP, 3L, 3L}, + {TSDataType.FLOAT, 1.25F, 1.25F}, + {TSDataType.DOUBLE, 2.5D, 2.5D}, + { + TSDataType.TEXT, + new Binary("text", StandardCharsets.UTF_8), + new Binary("text", StandardCharsets.UTF_8) + }, + { + TSDataType.STRING, + new Binary("string", StandardCharsets.UTF_8), + new Binary("string", StandardCharsets.UTF_8) + }, + {TSDataType.BLOB, new Binary(new byte[] {0x01, 0x23}), new Binary(new byte[] {0x01, 0x23})}, + {TSDataType.OBJECT, new Binary(BytesUtils.longToBytes(1L)), "(Object) 1 B"} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + Type type = Type.fromTsDataType(dataType); + Field typeField = type.getField(testCase[1]); + Assert.assertEquals(dataType, typeField.getDataType()); + Assert.assertEquals(testCase[2], type.getValue(typeField)); + + Field field = Field.getField(testCase[1], dataType); + Assert.assertEquals(dataType, field.getDataType()); + Assert.assertEquals(testCase[2], type.getValue(field)); + Assert.assertEquals(testCase[2], field.getObjectValue(dataType)); + } + + Assert.assertNull(Field.getField(null, TSDataType.INT32)); + Assert.assertNull(new Field(null).getObjectValue(TSDataType.INT32)); + Assert.assertEquals("1", Field.getField(1, TSDataType.INT32).getObjectValue(TSDataType.OBJECT)); + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + new Field(dataType).getObjectValue(dataType); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + + try { + Type.fromTsDataType(dataType).getField(new Object()); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testSetToField() { + Object[][] testCases = { + {TSDataType.BOOLEAN, new TsPrimitiveType.TsBoolean(true), true}, + {TSDataType.INT32, new TsPrimitiveType.TsInt(1), 1}, + { + TSDataType.DATE, + new TsPrimitiveType.TsInt(20260714, TSDataType.DATE), + LocalDate.of(2026, 7, 14) + }, + {TSDataType.INT64, new TsPrimitiveType.TsLong(2L), 2L}, + {TSDataType.TIMESTAMP, new TsPrimitiveType.TsLong(3L), 3L}, + {TSDataType.FLOAT, new TsPrimitiveType.TsFloat(1.25F), 1.25F}, + {TSDataType.DOUBLE, new TsPrimitiveType.TsDouble(2.5D), 2.5D}, + { + TSDataType.TEXT, + new TsPrimitiveType.TsBinary(new Binary("text", StandardCharsets.UTF_8)), + new Binary("text", StandardCharsets.UTF_8) + }, + { + TSDataType.STRING, + new TsPrimitiveType.TsBinary(new Binary("string", StandardCharsets.UTF_8)), + new Binary("string", StandardCharsets.UTF_8) + }, + { + TSDataType.BLOB, + new TsPrimitiveType.TsBinary(new Binary(new byte[] {0x01, 0x23})), + new Binary(new byte[] {0x01, 0x23}) + }, + { + TSDataType.OBJECT, + new TsPrimitiveType.TsBinary(new Binary(BytesUtils.longToBytes(1L))), + "(Object) 1 B" + } + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + TsPrimitiveType from = (TsPrimitiveType) testCase[1]; + Type type = Type.fromTsDataType(dataType); + + Field directTarget = new Field(dataType); + type.setTo(from, directTarget); + Assert.assertEquals(testCase[2], type.getValue(directTarget)); + + BatchData batchData = new BatchData(dataType); + batchData.putAnObject(1L, from.getValue()); + Field batchTarget = new Field(dataType); + type.setTo(batchData, batchTarget); + Assert.assertEquals(testCase[2], type.getValue(batchTarget)); + + if (from.getDataType() == dataType) { + Field factoryTarget = new Field(dataType); + Field.setTsPrimitiveValue(from, factoryTarget); + Assert.assertEquals(testCase[2], type.getValue(factoryTarget)); + } + } + + TsPrimitiveType vector = new TsPrimitiveType.TsVector(new TsPrimitiveType[0]); + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).setTo(vector, new Field(dataType)); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + + try { + Field.setTsPrimitiveValue(vector, new Field(TSDataType.VECTOR)); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + + BatchData vectorBatchData = new BatchData(TSDataType.VECTOR); + vectorBatchData.putVector(1L, new TsPrimitiveType[] {new TsPrimitiveType.TsInt(1)}); + Field vectorTarget = new Field(TSDataType.INT32); + Type.fromTsDataType(TSDataType.VECTOR).setTo(vectorBatchData, vectorTarget); + Assert.assertEquals(1, vectorTarget.getIntV()); + } + + @Test + public void testSetToArray() { + Column column = Mockito.mock(Column.class); + Binary binary = new Binary("value", TSFileConfig.STRING_CHARSET); + Mockito.when(column.getBoolean(1)).thenReturn(true); + Mockito.when(column.getInt(1)).thenReturn(1); + Mockito.when(column.getInt(2)).thenReturn(20260715); + Mockito.when(column.getLong(1)).thenReturn(2L); + Mockito.when(column.getFloat(1)).thenReturn(1.25F); + Mockito.when(column.getDouble(1)).thenReturn(2.5D); + Mockito.when(column.getBinary(1)).thenReturn(binary); + + Object[][] testCases = { + {TSDataType.BOOLEAN, 1, true}, + {TSDataType.INT32, 1, 1}, + {TSDataType.DATE, 2, LocalDate.of(2026, 7, 15)}, + {TSDataType.INT64, 1, 2L}, + {TSDataType.TIMESTAMP, 1, 2L}, + {TSDataType.FLOAT, 1, 1.25F}, + {TSDataType.DOUBLE, 1, 2.5D}, + {TSDataType.TEXT, 1, binary}, + {TSDataType.STRING, 1, binary}, + {TSDataType.BLOB, 1, binary}, + {TSDataType.OBJECT, 1, binary}, + {TSDataType.VECTOR, 1, 2L} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + Object target = type.createArray(1); + type.setTo(column, (int) testCase[1], target, 0); + Assert.assertEquals(testCase[2], Array.get(target, 0)); + } + + Object[][] columnTestCases = { + {TSDataType.BOOLEAN, 1, true, new boolean[1]}, + {TSDataType.INT32, 1, 1, new int[1]}, + {TSDataType.DATE, 2, 20260715, new int[1]}, + {TSDataType.INT64, 1, 2L, new long[1]}, + {TSDataType.TIMESTAMP, 1, 2L, new long[1]}, + {TSDataType.FLOAT, 1, 1.25F, new float[1]}, + {TSDataType.DOUBLE, 1, 2.5D, new double[1]}, + {TSDataType.TEXT, 1, binary, new Binary[1]}, + {TSDataType.STRING, 1, binary, new Binary[1]}, + {TSDataType.BLOB, 1, binary, new Binary[1]}, + {TSDataType.OBJECT, 1, binary, new Binary[1]} + }; + + for (Object[] testCase : columnTestCases) { + Column targetColumn = Mockito.mock(Column.class); + Object target = testCase[3]; + if (target instanceof boolean[]) { + Mockito.when(targetColumn.getBooleans()).thenReturn((boolean[]) target); + } else if (target instanceof int[]) { + Mockito.when(targetColumn.getInts()).thenReturn((int[]) target); + } else if (target instanceof long[]) { + Mockito.when(targetColumn.getLongs()).thenReturn((long[]) target); + } else if (target instanceof float[]) { + Mockito.when(targetColumn.getFloats()).thenReturn((float[]) target); + } else if (target instanceof double[]) { + Mockito.when(targetColumn.getDoubles()).thenReturn((double[]) target); + } else { + Mockito.when(targetColumn.getBinaries()).thenReturn((Binary[]) target); + } + Type.fromTsDataType((TSDataType) testCase[0]) + .setTo(column, (int) testCase[1], targetColumn, 0); + Assert.assertEquals(testCase[2], Array.get(target, 0)); + } + + try { + Type.fromTsDataType(TSDataType.VECTOR).setTo(column, 1, column, 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).setTo(column, 1, new Object[1], 0); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testSetNullToBinaryColumnRange() { + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Binary[] values = {Binary.EMPTY_VALUE, Binary.EMPTY_VALUE}; + Column column = Mockito.mock(Column.class); + Mockito.when(column.getBinaries()).thenReturn(values); + + Type.fromTsDataType(dataType).setTo(null, column, 0, values.length); + + Assert.assertArrayEquals(new Binary[] {null, null}, values); + } + } + + @Test + public void testDateAddValueWithIntArray() throws IOException { + Type dateType = Type.fromTsDataType(TSDataType.DATE); + int[] values = new int[2]; + + dateType.addValue(0, LocalDate.of(2026, 7, 14), values); + dateType.addValue(1, null, values); + + Assert.assertArrayEquals(new int[] {20260714, DateUtils.EMPTY_DATE_INT}, values); + + dateType.addValue(0, 20260715, values); + Assert.assertEquals(20260715, values[0]); + + Column column = Mockito.mock(Column.class); + Mockito.when(column.getInt(0)).thenReturn(20260715); + dateType.setTo(column, 0, values, 0); + Assert.assertEquals(20260715, values[0]); + + LocalDate[] localDates = new LocalDate[2]; + dateType.copyArrayElement(values, 0, localDates, 0); + dateType.copyArrayElement(localDates, 0, values, 0); + Assert.assertEquals(LocalDate.of(2026, 7, 15), localDates[0]); + Assert.assertEquals(20260715, values[0]); + Assert.assertTrue( + dateType.arrayEquals( + values, new LocalDate[] {LocalDate.of(2026, 7, 15), LocalDate.of(1000, 1, 1)}, 2)); + + Object copied = dateType.arrayCopyOf(values, 3); + Assert.assertTrue(copied instanceof int[]); + Assert.assertArrayEquals(new int[] {20260715, DateUtils.EMPTY_DATE_INT, 0}, (int[]) copied); + Assert.assertEquals(RamUsageEstimator.sizeOf(values), dateType.estimateArraySize(values)); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + dateType.serializeArray(values, values.length, new DataOutputStream(output)); + ByteBuffer buffer = ByteBuffer.wrap(output.toByteArray()); + Assert.assertEquals(20260715, buffer.getInt()); + Assert.assertEquals(DateUtils.EMPTY_DATE_INT, buffer.getInt()); + } + + @Test + public void testAddPoint() { + ResultSet resultSet = Mockito.mock(ResultSet.class); + IDeviceID deviceID = Mockito.mock(IDeviceID.class); + Mockito.when(resultSet.getBoolean("value")).thenReturn(true); + Mockito.when(resultSet.getInt("value")).thenReturn(1); + Mockito.when(resultSet.getLong("value")).thenReturn(2L); + Mockito.when(resultSet.getFloat("value")).thenReturn(1.25F); + Mockito.when(resultSet.getDouble("value")).thenReturn(2.5D); + Mockito.when(resultSet.getString("value")).thenReturn("text"); + Mockito.when(resultSet.getBinary("value")).thenReturn(new byte[] {0x01, 0x23}); + + Object[][] testCases = { + {TSDataType.BOOLEAN, true}, + {TSDataType.INT32, 1}, + {TSDataType.DATE, 1}, + {TSDataType.INT64, 2L}, + {TSDataType.TIMESTAMP, 2L}, + {TSDataType.FLOAT, 1.25F}, + {TSDataType.DOUBLE, 2.5D}, + {TSDataType.TEXT, new Binary("text", StandardCharsets.UTF_8)}, + {TSDataType.STRING, new Binary("text", StandardCharsets.UTF_8)}, + {TSDataType.BLOB, new Binary(new byte[] {0x01, 0x23})}, + {TSDataType.OBJECT, new Binary(new byte[] {0x01, 0x23})} + }; + + for (Object[] testCase : testCases) { + TSRecord record = new TSRecord(deviceID, 1L); + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + type.addPoint(record, "value", resultSet); + Assert.assertEquals(1, record.dataPointList.size()); + Assert.assertEquals(testCase[1], record.dataPointList.get(0).getValue()); + + TSRecord fieldRecord = new TSRecord(deviceID, 1L); + type.addPoint(fieldRecord, "value", type.getField(testCase[1])); + Assert.assertEquals(1, fieldRecord.dataPointList.size()); + Assert.assertEquals(testCase[1], fieldRecord.dataPointList.get(0).getValue()); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + TSRecord record = new TSRecord(deviceID, 1L); + try { + Type.fromTsDataType(dataType).addPoint(record, "value", resultSet); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + + try { + Type.fromTsDataType(dataType).addPoint(record, "value", new Field(dataType)); + Assert.fail("Expected UnSupportedDataTypeException"); + } catch (UnSupportedDataTypeException ignored) { + // Expected. + } + } + } + + @Test + public void testCalcTypeSizeFromObject() { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, true, Byte.BYTES}, + {TSDataType.INT32, 1, Integer.BYTES}, + {TSDataType.DATE, 20260713, Integer.BYTES}, + {TSDataType.INT64, 1L, Long.BYTES}, + {TSDataType.TIMESTAMP, 1L, Long.BYTES}, + {TSDataType.FLOAT, 1.0F, Float.BYTES}, + {TSDataType.DOUBLE, 1.0D, Double.BYTES}, + {TSDataType.TEXT, binary, Integer.BYTES + binary.getLength()}, + {TSDataType.STRING, binary, Integer.BYTES + binary.getLength()}, + {TSDataType.BLOB, binary, Integer.BYTES + binary.getLength()}, + {TSDataType.OBJECT, binary, Integer.BYTES + binary.getLength()} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + Object value = testCase[1]; + int expectedSize = (int) testCase[2]; + Assert.assertEquals(expectedSize, Type.fromTsDataType(dataType).calcTypeSize(value)); + } + } + + @Test + public void testDeserializeColumn() { + boolean[] nullIndicators = {true, false}; + + ByteBuffer booleanBuffer = ByteBuffer.wrap(new byte[] {(byte) 0b1000_0000, 0x01}); + Column booleanColumn = + (Column) + Type.fromTsDataType(TSDataType.BOOLEAN) + .deserializeColumn(booleanBuffer, 2, nullIndicators); + Assert.assertTrue(booleanColumn.isNull(0)); + Assert.assertTrue(booleanColumn.getBoolean(1)); + Assert.assertEquals(1, booleanBuffer.position()); + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT32, TSDataType.DATE}) { + Column column = + (Column) + Type.fromTsDataType(dataType) + .deserializeColumn(intBuffer(20260714), 2, nullIndicators); + Assert.assertEquals(dataType, column.getDataType()); + Assert.assertTrue(column.isNull(0)); + Assert.assertEquals(20260714, column.getInt(1)); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.INT64, TSDataType.TIMESTAMP}) { + Column column = + (Column) + Type.fromTsDataType(dataType) + .deserializeColumn(longBuffer(123456789L), 2, nullIndicators); + Assert.assertTrue(column.isNull(0)); + Assert.assertEquals(123456789L, column.getLong(1)); + } + + Column floatColumn = + (Column) + Type.fromTsDataType(TSDataType.FLOAT) + .deserializeColumn(intBuffer(Float.floatToIntBits(1.25F)), 2, nullIndicators); + Assert.assertTrue(floatColumn.isNull(0)); + Assert.assertEquals(1.25F, floatColumn.getFloat(1), 0); + + Column doubleColumn = + (Column) + Type.fromTsDataType(TSDataType.DOUBLE) + .deserializeColumn(longBuffer(Double.doubleToLongBits(2.5D)), 2, nullIndicators); + Assert.assertTrue(doubleColumn.isNull(0)); + Assert.assertEquals(2.5D, doubleColumn.getDouble(1), 0); + + Binary binary = new Binary("test", StandardCharsets.UTF_8); + for (TSDataType dataType : + new TSDataType[] {TSDataType.TEXT, TSDataType.STRING, TSDataType.BLOB, TSDataType.OBJECT}) { + Column column = + (Column) + Type.fromTsDataType(dataType) + .deserializeColumn(binaryBuffer(binary), 2, nullIndicators); + Assert.assertTrue(column.isNull(0)); + Assert.assertEquals(binary, column.getBinary(1)); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + Type.fromTsDataType(dataType).deserializeColumn(ByteBuffer.allocate(0), 0, null); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testEstimateArraySize() { + int size = 10; + Object[][] testCases = { + {TSDataType.BOOLEAN, RamUsageEstimator.sizeOfBooleanArray(size)}, + {TSDataType.INT32, RamUsageEstimator.sizeOfIntArray(size)}, + {TSDataType.DATE, RamUsageEstimator.sizeOfObjectArray(size)}, + {TSDataType.INT64, RamUsageEstimator.sizeOfLongArray(size)}, + {TSDataType.TIMESTAMP, RamUsageEstimator.sizeOfLongArray(size)}, + {TSDataType.FLOAT, RamUsageEstimator.sizeOfFloatArray(size)}, + {TSDataType.DOUBLE, RamUsageEstimator.sizeOfDoubleArray(size)}, + {TSDataType.TEXT, RamUsageEstimator.sizeOfObjectArray(size)}, + {TSDataType.STRING, RamUsageEstimator.sizeOfObjectArray(size)}, + {TSDataType.BLOB, RamUsageEstimator.sizeOfObjectArray(size)}, + {TSDataType.OBJECT, RamUsageEstimator.sizeOfObjectArray(size)}, + {TSDataType.VECTOR, RamUsageEstimator.sizeOfLongArray(size)} + }; + + for (Object[] testCase : testCases) { + Assert.assertEquals( + testCase[1], Type.fromTsDataType((TSDataType) testCase[0]).estimateArraySize(size)); + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).estimateArraySize(size); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testEstimateArraySizeWithArray() { + boolean[] booleans = {false, true}; + int[] ints = {0, 1}; + LocalDate[] dates = {null, LocalDate.of(2026, 7, 15)}; + long[] longs = {0L, 1L}; + float[] floats = {0.0F, 1.0F}; + double[] doubles = {0.0D, 1.0D}; + Binary[] binaries = {null, new Binary("test", StandardCharsets.UTF_8)}; + + assertEstimateArraySize(TSDataType.BOOLEAN, booleans, RamUsageEstimator.sizeOf(booleans)); + assertEstimateArraySize(TSDataType.INT32, ints, RamUsageEstimator.sizeOf(ints)); + assertEstimateArraySize(TSDataType.DATE, dates, RamUsageEstimator.sizeOf(dates)); + assertEstimateArraySize(TSDataType.INT64, longs, RamUsageEstimator.sizeOf(longs)); + assertEstimateArraySize(TSDataType.TIMESTAMP, longs, RamUsageEstimator.sizeOf(longs)); + assertEstimateArraySize(TSDataType.FLOAT, floats, RamUsageEstimator.sizeOf(floats)); + assertEstimateArraySize(TSDataType.DOUBLE, doubles, RamUsageEstimator.sizeOf(doubles)); + assertEstimateArraySize(TSDataType.TEXT, binaries, RamUsageEstimator.sizeOf(binaries)); + assertEstimateArraySize(TSDataType.STRING, binaries, RamUsageEstimator.sizeOf(binaries)); + assertEstimateArraySize(TSDataType.BLOB, binaries, RamUsageEstimator.sizeOf(binaries)); + assertEstimateArraySize(TSDataType.OBJECT, binaries, RamUsageEstimator.sizeOf(binaries)); + assertEstimateArraySize(TSDataType.VECTOR, longs, RamUsageEstimator.sizeOf(longs)); + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).estimateArraySize(new Object[0]); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + private void assertEstimateArraySize(TSDataType dataType, Object array, long expected) { + Assert.assertEquals(expected, Type.fromTsDataType(dataType).estimateArraySize(array)); + } + + @Test + public void testCopyArrayElement() { + LocalDate date = LocalDate.of(2026, 7, 15); + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, new boolean[] {false, true}}, + {TSDataType.INT32, new int[] {0, 1}}, + {TSDataType.DATE, new LocalDate[] {null, date}}, + {TSDataType.INT64, new long[] {0L, 1L}}, + {TSDataType.TIMESTAMP, new long[] {0L, 1L}}, + {TSDataType.FLOAT, new float[] {0.0F, 1.0F}}, + {TSDataType.DOUBLE, new double[] {0.0D, 1.0D}}, + {TSDataType.TEXT, new Binary[] {null, binary}}, + {TSDataType.STRING, new Binary[] {null, binary}}, + {TSDataType.BLOB, new Binary[] {null, binary}}, + {TSDataType.OBJECT, new Binary[] {null, binary}}, + {TSDataType.VECTOR, new long[] {0L, 1L}} + }; + + for (Object[] testCase : testCases) { + Type type = Type.fromTsDataType((TSDataType) testCase[0]); + Object source = testCase[1]; + Object target = type.createArray(2); + + type.copyArrayElement(source, 1, target, 0); + type.copyArrayElement(source, 0, target, 1); + + Assert.assertEquals(Array.get(source, 1), Array.get(target, 0)); + Assert.assertEquals(Array.get(source, 0), Array.get(target, 1)); + + Object expanded = type.arrayCopyOf(source, 3); + Assert.assertEquals(3, Array.getLength(expanded)); + Assert.assertEquals(Array.get(source, 0), Array.get(expanded, 0)); + Assert.assertEquals(Array.get(source, 1), Array.get(expanded, 1)); + + Object truncated = type.arrayCopyOf(source, 1); + Assert.assertEquals(1, Array.getLength(truncated)); + Assert.assertEquals(Array.get(source, 0), Array.get(truncated, 0)); + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).arrayCopyOf(new Object[0], 1); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testEstimateValueSize() { + Object[][] testCases = { + {TSDataType.BOOLEAN, (long) Byte.BYTES}, + {TSDataType.INT32, (long) Integer.BYTES}, + {TSDataType.DATE, (long) Integer.BYTES}, + {TSDataType.INT64, (long) Long.BYTES}, + {TSDataType.TIMESTAMP, (long) Long.BYTES}, + {TSDataType.FLOAT, (long) Float.BYTES}, + {TSDataType.DOUBLE, (long) Double.BYTES}, + {TSDataType.TEXT, (long) RamUsageEstimator.NUM_BYTES_OBJECT_REF}, + {TSDataType.STRING, (long) RamUsageEstimator.NUM_BYTES_OBJECT_REF}, + {TSDataType.BLOB, (long) RamUsageEstimator.NUM_BYTES_OBJECT_REF}, + {TSDataType.OBJECT, (long) RamUsageEstimator.NUM_BYTES_OBJECT_REF}, + {TSDataType.VECTOR, (long) RamUsageEstimator.NUM_BYTES_OBJECT_REF} + }; + + for (Object[] testCase : testCases) { + Assert.assertEquals( + testCase[1], Type.fromTsDataType((TSDataType) testCase[0]).estimateValueSize()); + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).estimateValueSize(); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + + @Test + public void testGetOneItemMaxSize() { + Object[][] testCases = { + {TSDataType.BOOLEAN, Byte.BYTES}, + {TSDataType.INT32, Integer.BYTES}, + {TSDataType.DATE, Integer.BYTES}, + {TSDataType.INT64, Long.BYTES}, + {TSDataType.TIMESTAMP, Long.BYTES}, + {TSDataType.FLOAT, Float.BYTES}, + {TSDataType.DOUBLE, Double.BYTES}, + {TSDataType.TEXT, Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * VALUE_LENGTH}, + {TSDataType.STRING, Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * VALUE_LENGTH}, + {TSDataType.BLOB, Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * VALUE_LENGTH}, + {TSDataType.OBJECT, Integer.BYTES + TSFileConfig.BYTE_SIZE_PER_CHAR * VALUE_LENGTH} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + int expectedSize = (int) testCase[1]; + Assert.assertEquals( + expectedSize, Type.fromTsDataType(dataType).getOneItemMaxSize(VALUE_LENGTH)); + Assert.assertEquals( + expectedSize, new PlainEncoder(dataType, VALUE_LENGTH).getOneItemMaxSize()); + } + + for (TSDataType dataType : new TSDataType[] {TSDataType.VECTOR, TSDataType.UNKNOWN}) { + try { + new PlainEncoder(dataType, VALUE_LENGTH).getOneItemMaxSize(); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException ignored) { + // Expected. + } + } + } + + @Test + public void testCreateStatistics() { + for (TSDataType dataType : + new TSDataType[] { + TSDataType.BOOLEAN, + TSDataType.INT32, + TSDataType.DATE, + TSDataType.INT64, + TSDataType.TIMESTAMP, + TSDataType.FLOAT, + TSDataType.DOUBLE, + TSDataType.TEXT, + TSDataType.STRING, + TSDataType.BLOB, + TSDataType.OBJECT, + TSDataType.VECTOR + }) { + Type type = Type.fromTsDataType(dataType); + Assert.assertEquals(dataType, type.createStatistics().getType()); + Assert.assertEquals(dataType, Statistics.getStatsByType(dataType).getType()); + Assert.assertTrue(type.getStatisticsInstanceSize() > 0); + Assert.assertEquals(type.getStatisticsInstanceSize(), Statistics.getSizeByType(dataType)); + } + + try { + Type.fromTsDataType(TSDataType.UNKNOWN).createStatistics(); + Assert.fail("Expected UnknownColumnTypeException"); + } catch (UnknownColumnTypeException ignored) { + // Expected. + } + + try { + Statistics.getSizeByType(TSDataType.UNKNOWN); + Assert.fail("Expected UnknownColumnTypeException"); + } catch (UnknownColumnTypeException ignored) { + // Expected. + } + } + + @Test + public void testUpdateStatistics() { + Binary binary = new Binary("test", StandardCharsets.UTF_8); + Object[][] testCases = { + {TSDataType.BOOLEAN, new TsPrimitiveType.TsBoolean(true)}, + {TSDataType.INT32, new TsPrimitiveType.TsInt(1)}, + {TSDataType.DATE, new TsPrimitiveType.TsInt(20260714, TSDataType.DATE)}, + {TSDataType.INT64, new TsPrimitiveType.TsLong(1L)}, + {TSDataType.TIMESTAMP, new TsPrimitiveType.TsLong(1L)}, + {TSDataType.FLOAT, new TsPrimitiveType.TsFloat(1.0F)}, + {TSDataType.DOUBLE, new TsPrimitiveType.TsDouble(1.0D)}, + {TSDataType.TEXT, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.STRING, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.BLOB, new TsPrimitiveType.TsBinary(binary)}, + {TSDataType.OBJECT, new TsPrimitiveType.TsBinary(binary)} + }; + + for (Object[] testCase : testCases) { + TSDataType dataType = (TSDataType) testCase[0]; + TsPrimitiveType value = (TsPrimitiveType) testCase[1]; + Type type = Type.fromTsDataType(dataType); + Statistics statistics = Statistics.getStatsByType(dataType); + type.update(statistics, 100L, value); + Assert.assertEquals(1, statistics.getCount()); + Assert.assertEquals(100L, statistics.getStartTime()); + Assert.assertEquals(100L, statistics.getEndTime()); + Assert.assertFalse(statistics.isEmpty()); + + BatchData batchData = new BatchData(dataType); + batchData.putAnObject(200L, value.getValue()); + Assert.assertEquals(value.getValue(), type.getCurrentValue(batchData)); + Assert.assertEquals(value.getValue(), batchData.currentValue()); + Statistics batchStatistics = Statistics.getStatsByType(dataType); + type.update(batchStatistics, batchData); + Assert.assertEquals(1, batchStatistics.getCount()); + Assert.assertEquals(200L, batchStatistics.getStartTime()); + Assert.assertEquals(200L, batchStatistics.getEndTime()); + Assert.assertFalse(batchStatistics.isEmpty()); + + TsBlockBuilder blockBuilder = new TsBlockBuilder(Arrays.asList(dataType, dataType)); + blockBuilder.getTimeColumnBuilder().writeLong(250L); + blockBuilder.getColumnBuilder(0).writeObject(value.getValue()); + blockBuilder.getColumnBuilder(1).writeObject(value.getValue()); + blockBuilder.declarePosition(); + blockBuilder.getTimeColumnBuilder().writeLong(300L); + blockBuilder.getColumnBuilder(0).writeObject(value.getValue()); + blockBuilder.getColumnBuilder(1).writeObject(value.getValue()); + blockBuilder.declarePosition(); + TsBlock block = blockBuilder.build(); + Statistics blockStatistics = Statistics.getStatsByType(dataType); + type.update(blockStatistics, block, 1, 1); + Assert.assertEquals(1, blockStatistics.getCount()); + Assert.assertEquals(300L, blockStatistics.getStartTime()); + Assert.assertEquals(300L, blockStatistics.getEndTime()); + Assert.assertFalse(blockStatistics.isEmpty()); + } + } + + @Test + public void testGetCurrentValueForVectorAndUnknown() { + TsPrimitiveType[] vector = {new TsPrimitiveType.TsLong(1L)}; + BatchData batchData = new BatchData(TSDataType.VECTOR); + batchData.putAnObject(1L, vector); + Assert.assertSame(vector, Type.fromTsDataType(TSDataType.VECTOR).getCurrentValue(batchData)); + Assert.assertSame(vector, batchData.currentValue()); + Assert.assertNull(Type.fromTsDataType(TSDataType.UNKNOWN).getCurrentValue(null)); + } + + private static ByteBuffer intBuffer(int value) { + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); + buffer.putInt(value); + return buffer.flip(); + } + + private static ByteBuffer longBuffer(long value) { + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(value); + return buffer.flip(); + } + + private static ByteBuffer binaryBuffer(Binary value) { + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + value.getLength()); + buffer.putInt(value.getLength()); + buffer.put(value.getValues()); + return buffer.flip(); + } +} diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/filter/TsBlockFilterTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/filter/TsBlockFilterTest.java index a4b7f3c8d..a6c6e0599 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/filter/TsBlockFilterTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/filter/TsBlockFilterTest.java @@ -24,6 +24,8 @@ import org.apache.tsfile.read.common.block.column.IntColumn; import org.apache.tsfile.read.common.block.column.LongColumn; import org.apache.tsfile.read.common.block.column.TimeColumn; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.IntTypeService; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.read.filter.factory.ValueFilterApi; import org.apache.tsfile.utils.Binary; @@ -41,6 +43,21 @@ public class TsBlockFilterTest { private static final int COL_NUM = 3; private static final int ROW_NUM = 7; + private static final IntTypeService GET_MEASUREMENT_ID_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> 0; + case INT32 -> 1; + case INT64 -> 2; + case FLOAT -> 3; + case DOUBLE -> 4; + case TEXT -> 5; + case BLOB, DATE, OBJECT, ROW, STRING, TIMESTAMP, UNKNOWN, VECTOR -> -1; + }; + + static { + GET_MEASUREMENT_ID_SERVICE.check(); + } private TsBlock tsBlock; @@ -49,22 +66,7 @@ public class TsBlockFilterTest { private boolean[] selection = new boolean[COL_NUM]; public int getMeasurementId(TSDataType type) { - switch (type) { - case BOOLEAN: - return 0; - case INT32: - return 1; - case INT64: - return 2; - case FLOAT: - return 3; - case DOUBLE: - return 4; - case TEXT: - return 5; - default: - return -1; - } + return GET_MEASUREMENT_ID_SERVICE.call(Type.fromTsDataType(type)); } @Before diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/reader/PageReaderTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/reader/PageReaderTest.java index 7b0258f2d..7c9a38bd8 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/reader/PageReaderTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/reader/PageReaderTest.java @@ -36,6 +36,8 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.BatchData; import org.apache.tsfile.read.common.TimeRange; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.read.reader.page.PageReader; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.write.page.PageWriter; @@ -51,6 +53,22 @@ public class PageReaderTest { private static final int POINTS_COUNT_IN_ONE_PAGE = 1000000; + private static final TypeService WRITE_PAGE_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> (writer, time, value) -> writer.write(time, (Boolean) value); + case INT32 -> (writer, time, value) -> writer.write(time, (Integer) value); + case INT64 -> (writer, time, value) -> writer.write(time, (Long) value); + case FLOAT -> (writer, time, value) -> writer.write(time, (Float) value); + case DOUBLE -> (writer, time, value) -> writer.write(time, (Double) value); + case TEXT -> (writer, time, value) -> writer.write(time, (Binary) value); + case BLOB, DATE, OBJECT, ROW, STRING, TIMESTAMP, UNKNOWN, VECTOR -> + (writer, time, value) -> {}; + }; + + static { + WRITE_PAGE_VALUE_SERVICE.check(); + } @Test public void testLong() { @@ -272,33 +290,21 @@ public void testDelete(TSDataType dataType) { } private void writeData() { + PageValueWriter valueWriter = WRITE_PAGE_VALUE_SERVICE.call(Type.fromTsDataType(dataType)); for (int i = 0; i < count; i++) { - switch (dataType) { - case BOOLEAN: - pageWriter.write(i, (Boolean) generateValueByIndex(i)); - break; - case INT32: - pageWriter.write(i, (Integer) generateValueByIndex(i)); - break; - case INT64: - pageWriter.write(i, (Long) generateValueByIndex(i)); - break; - case FLOAT: - pageWriter.write(i, (Float) generateValueByIndex(i)); - break; - case DOUBLE: - pageWriter.write(i, (Double) generateValueByIndex(i)); - break; - case TEXT: - pageWriter.write(i, (Binary) generateValueByIndex(i)); - break; - } + valueWriter.write(pageWriter, i, generateValueByIndex(i)); } } public abstract Object generateValueByIndex(int i); } + @FunctionalInterface + private interface PageValueWriter { + + void write(PageWriter writer, long time, Object value); + } + @Test public void testPageDelete() { LoopWriteReadTest test = diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/reader/TsFileLastReaderTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/reader/TsFileLastReaderTest.java index bfc55868d..efa9871b4 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/reader/TsFileLastReaderTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/reader/TsFileLastReaderTest.java @@ -24,6 +24,8 @@ import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.IDeviceID.Factory; import org.apache.tsfile.read.TimeValuePair; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.TsPrimitiveType; @@ -61,6 +63,34 @@ public class TsFileLastReaderTest { Arrays.asList(TSDataType.INT64, TSDataType.BLOB); private static final Map typeAddValueFunctions = new HashMap<>(); + private static final TypeService ADD_OFFSET_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT64 -> + (tablet, row, column, offset) -> tablet.addValue(row, column, (long) row + offset); + case BLOB -> + (tablet, row, column, offset) -> + tablet.addValue( + row, + column, + Long.toBinaryString(row + offset).getBytes(StandardCharsets.UTF_8)); + case BOOLEAN, + DATE, + DOUBLE, + FLOAT, + INT32, + OBJECT, + ROW, + STRING, + TEXT, + TIMESTAMP, + UNKNOWN, + VECTOR -> + (tablet, row, column, offset) -> { + throw new IllegalArgumentException( + "Unsupported TSDataType " + type.getTypeEnum()); + }; + }; static { typeAddValueFunctions.put( @@ -70,6 +100,7 @@ public class TsFileLastReaderTest { ((tablet, row, column) -> tablet.addValue( row, column, Long.toBinaryString(row).getBytes(StandardCharsets.UTF_8)))); + ADD_OFFSET_VALUE_SERVICE.check(); } private final String filePath = "target/test.tsfile"; @@ -142,20 +173,10 @@ private void createFileWithLastEmptyChunks(int deviceNum, int measurementNum, in } for (int j = 0; j < measurementNum / 2; j++) { TSDataType tsDataType = dataTypes.get(j % dataTypes.size()); + OffsetTabletAddValueFunction addValueFunction = + ADD_OFFSET_VALUE_SERVICE.call(Type.fromTsDataType(tsDataType)); for (int k = 0; k < seriesPointNum; k++) { - switch (tsDataType) { - case INT64: - tablet.addValue(k, j, (long) k + seriesPointNum / 2); - break; - case BLOB: - tablet.addValue( - k, - j, - Long.toBinaryString(k + seriesPointNum / 2).getBytes(StandardCharsets.UTF_8)); - break; - default: - throw new IllegalArgumentException("Unsupported TSDataType " + tsDataType); - } + addValueFunction.addValue(tablet, k, j, seriesPointNum / 2); } } writer.writeTree(tablet); @@ -163,6 +184,12 @@ private void createFileWithLastEmptyChunks(int deviceNum, int measurementNum, in } } + @FunctionalInterface + private interface OffsetTabletAddValueFunction { + + void addValue(Tablet tablet, int row, int column, int offset); + } + private void createAlignedFileWithObject(int seriesPointNum) throws IOException, WriteProcessException { try (TsFileWriter writer = new TsFileWriter(file)) { @@ -332,13 +359,16 @@ public void testLastEmptyPage() throws Exception { alignedChunkWriter.write( 0, new TsPrimitiveType[] { - TsPrimitiveType.getByType(TSDataType.INT64, 0L), - TsPrimitiveType.getByType( - TSDataType.BLOB, new Binary("0".getBytes(StandardCharsets.UTF_8))) + Type.fromTsDataType(TSDataType.INT64).getTsPrimitiveType(0L), + Type.fromTsDataType(TSDataType.BLOB) + .getTsPrimitiveType(new Binary("0".getBytes(StandardCharsets.UTF_8))) }); alignedChunkWriter.sealCurrentPage(); alignedChunkWriter.write( - 1, new TsPrimitiveType[] {TsPrimitiveType.getByType(TSDataType.INT64, 1L), null}); + 1, + new TsPrimitiveType[] { + Type.fromTsDataType(TSDataType.INT64).getTsPrimitiveType(1L), null + }); alignedChunkWriter.writeToFileWriter(ioWriter); ioWriter.endChunkGroup(); diff --git a/java/tsfile/src/test/java/org/apache/tsfile/tableview/TableViewTest.java b/java/tsfile/src/test/java/org/apache/tsfile/tableview/TableViewTest.java index b55473681..434fd50cf 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/tableview/TableViewTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/tableview/TableViewTest.java @@ -32,6 +32,8 @@ import org.apache.tsfile.read.common.Path; import org.apache.tsfile.read.common.RowRecord; import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.read.controller.CachedChunkLoaderImpl; import org.apache.tsfile.read.controller.MetadataQuerierByFileImpl; import org.apache.tsfile.read.expression.QueryExpression; @@ -68,6 +70,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.IntFunction; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; @@ -81,6 +84,29 @@ public class TableViewTest { private static final int measurementSchemaNum = 5; private static TableSchema testTableSchema; private static int numTimestampPerDevice = 10; + private static final TypeService> GET_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT64 -> i -> (long) i; + case TEXT -> i -> new Binary(String.valueOf(i), StandardCharsets.UTF_8); + case BLOB, + BOOLEAN, + DATE, + DOUBLE, + FLOAT, + INT32, + OBJECT, + ROW, + STRING, + TIMESTAMP, + UNKNOWN, + VECTOR -> + i -> i; + }; + + static { + GET_VALUE_SERVICE.check(); + } @Before public void setUp() throws Exception { @@ -505,14 +531,7 @@ public static Tablet genTablet(TableSchema tableSchema, int offset, int deviceNu } public static Object getValue(TSDataType dataType, int i) { - switch (dataType) { - case INT64: - return (long) i; - case TEXT: - return new Binary(String.valueOf(i), StandardCharsets.UTF_8); - default: - return i; - } + return GET_VALUE_SERVICE.call(Type.fromTsDataType(dataType)).apply(i); } public static TableSchema genTableSchema(int tableNum) { diff --git a/java/tsfile/src/test/java/org/apache/tsfile/utils/RecordUtils.java b/java/tsfile/src/test/java/org/apache/tsfile/utils/RecordUtils.java index 7e623590b..f67f82a0b 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/utils/RecordUtils.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/utils/RecordUtils.java @@ -21,13 +21,10 @@ import org.apache.tsfile.common.constant.JsonFormatConstant; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID.Factory; +import org.apache.tsfile.i18n.Messages; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.write.UnSupportedDataTypeException; import org.apache.tsfile.write.record.TSRecord; -import org.apache.tsfile.write.record.datapoint.BooleanDataPoint; -import org.apache.tsfile.write.record.datapoint.DoubleDataPoint; -import org.apache.tsfile.write.record.datapoint.FloatDataPoint; -import org.apache.tsfile.write.record.datapoint.IntDataPoint; -import org.apache.tsfile.write.record.datapoint.LongDataPoint; -import org.apache.tsfile.write.record.datapoint.StringDataPoint; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.apache.tsfile.write.schema.Schema; @@ -56,7 +53,7 @@ public static TSRecord parseSimpleTupleRecord(String str, Schema schema) { try { timestamp = Long.parseLong(items[1].trim()); } catch (NumberFormatException e) { - LOG.warn("given timestamp is illegal:{}", str); + LOG.warn(Messages.get("log.utils.record_timestamp_illegal"), str); // return a TSRecord without any data points return new TSRecord(deviceId, -1); } @@ -75,7 +72,7 @@ public static TSRecord parseSimpleTupleRecord(String str, Schema schema) { ? null : measurementGroup.getMeasurementSchemaMap().get(measurementId); if (measurementSchema == null) { - LOG.warn("measurementId:{},type not found, pass", measurementId); + LOG.warn(Messages.get("log.utils.record_measurement_type_not_found"), measurementId); continue; } type = measurementSchema.getType(); @@ -84,35 +81,11 @@ public static TSRecord parseSimpleTupleRecord(String str, Schema schema) { // TSRecord if (!"".equals(value)) { try { - switch (type) { - case INT32: - case DATE: - ret.addTuple(new IntDataPoint(measurementId, Integer.parseInt(value))); - break; - case INT64: - case TIMESTAMP: - ret.addTuple(new LongDataPoint(measurementId, Long.parseLong(value))); - break; - case FLOAT: - ret.addTuple(new FloatDataPoint(measurementId, Float.parseFloat(value))); - break; - case DOUBLE: - ret.addTuple(new DoubleDataPoint(measurementId, Double.parseDouble(value))); - break; - case BOOLEAN: - ret.addTuple(new BooleanDataPoint(measurementId, Boolean.parseBoolean(value))); - break; - case TEXT: - case BLOB: - case STRING: - ret.addTuple(new StringDataPoint(measurementId, BytesUtils.valueOf(items[i + 1]))); - break; - default: - LOG.warn("unsupported data type:{}", type); - break; - } + ret.addTuple(Type.fromTsDataType(type).getDataPoint(measurementId, value)); } catch (NumberFormatException e) { - LOG.warn("parsing measurement meets error, omit it", e); + LOG.warn(Messages.get("log.utils.record_parse_measurement_error"), e); + } catch (UnSupportedDataTypeException e) { + LOG.warn(Messages.get("log.utils.record_unsupported_data_type"), type); } } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/utils/TsPrimitiveTypeTest.java b/java/tsfile/src/test/java/org/apache/tsfile/utils/TsPrimitiveTypeTest.java index d74847b38..7c55269c7 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/utils/TsPrimitiveTypeTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/utils/TsPrimitiveTypeTest.java @@ -21,12 +21,15 @@ import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.TimeValuePair; +import org.apache.tsfile.read.common.type.RowType; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.TsPrimitiveType.TsBinary; import org.apache.tsfile.utils.TsPrimitiveType.TsBoolean; import org.apache.tsfile.utils.TsPrimitiveType.TsDouble; import org.apache.tsfile.utils.TsPrimitiveType.TsFloat; import org.apache.tsfile.utils.TsPrimitiveType.TsInt; import org.apache.tsfile.utils.TsPrimitiveType.TsLong; +import org.apache.tsfile.utils.TsPrimitiveType.TsVector; import org.junit.Assert; import org.junit.Test; @@ -35,32 +38,48 @@ public class TsPrimitiveTypeTest { @Test public void testNewAndGet() { - TsPrimitiveType intValue = TsPrimitiveType.getByType(TSDataType.INT32, 123); + TsPrimitiveType intValue = Type.fromTsDataType(TSDataType.INT32).getTsPrimitiveType(123); Assert.assertEquals(new TsInt(123), intValue); Assert.assertEquals(123, intValue.getInt()); - TsPrimitiveType longValue = TsPrimitiveType.getByType(TSDataType.INT64, 456L); + TsPrimitiveType longValue = Type.fromTsDataType(TSDataType.INT64).getTsPrimitiveType(456L); Assert.assertEquals(new TsLong(456), longValue); Assert.assertEquals(456L, longValue.getLong()); - TsPrimitiveType floatValue = TsPrimitiveType.getByType(TSDataType.FLOAT, 123f); + TsPrimitiveType floatValue = Type.fromTsDataType(TSDataType.FLOAT).getTsPrimitiveType(123f); Assert.assertEquals(new TsFloat(123), floatValue); Assert.assertEquals(123f, floatValue.getFloat(), 0.01); - TsPrimitiveType doubleValue = TsPrimitiveType.getByType(TSDataType.DOUBLE, 456d); + TsPrimitiveType doubleValue = Type.fromTsDataType(TSDataType.DOUBLE).getTsPrimitiveType(456d); Assert.assertEquals(new TsDouble(456), doubleValue); Assert.assertEquals(456d, doubleValue.getDouble(), 0.01); TsPrimitiveType textValue = - TsPrimitiveType.getByType(TSDataType.TEXT, new Binary("123", TSFileConfig.STRING_CHARSET)); + Type.fromTsDataType(TSDataType.TEXT) + .getTsPrimitiveType(new Binary("123", TSFileConfig.STRING_CHARSET)); Assert.assertEquals(new TsBinary(new Binary("123", TSFileConfig.STRING_CHARSET)), textValue); Assert.assertEquals(new Binary("123", TSFileConfig.STRING_CHARSET), textValue.getBinary()); - TsPrimitiveType booleanValue = TsPrimitiveType.getByType(TSDataType.BOOLEAN, true); + TsPrimitiveType booleanValue = Type.fromTsDataType(TSDataType.BOOLEAN).getTsPrimitiveType(true); Assert.assertEquals(new TsBoolean(true), booleanValue); Assert.assertTrue(booleanValue.getBoolean()); } + @Test + public void testTypeSpecificCreation() { + TsPrimitiveType emptyDate = Type.fromTsDataType(TSDataType.DATE).getTsPrimitiveType(); + Assert.assertEquals(TSDataType.DATE, emptyDate.getDataType()); + + TsPrimitiveType date = Type.fromTsDataType(TSDataType.DATE).getTsPrimitiveType(20260713); + Assert.assertEquals(20260713, date.getInt()); + Assert.assertEquals(TSDataType.DATE, date.getDataType()); + + TsPrimitiveType[] values = {date, null}; + TsPrimitiveType vector = RowType.anonymousRow().getTsPrimitiveType(values); + Assert.assertSame(values, vector.getVector()); + Assert.assertNull(RowType.anonymousRow().getTsPrimitiveType().getVector()); + } + @Test public void testCompareWithNullValue() { TimeValuePair timeValuePair1 = @@ -75,4 +94,66 @@ public void testCompareWithNullValue() { new TsPrimitiveType[] {new TsBoolean(true), null, new TsInt(1), null})); Assert.assertFalse(timeValuePair1.equals(timeValuePair2)); } + + @Test + public void testCopy() { + TsBoolean booleanValue = new TsBoolean(true); + TsBoolean booleanCopy = new TsBoolean(); + booleanCopy.copy(booleanValue); + booleanValue.setBoolean(false); + Assert.assertTrue(booleanCopy.getBoolean()); + + TsInt dateValue = new TsInt(20260722, TSDataType.DATE); + TsInt dateCopy = new TsInt(); + dateCopy.copy(dateValue); + dateValue.setInt(20260723); + Assert.assertEquals(20260722, dateCopy.getInt()); + Assert.assertEquals(TSDataType.DATE, dateCopy.getDataType()); + + TsLong longValue = new TsLong(1L); + TsLong longCopy = new TsLong(); + longCopy.copy(longValue); + longValue.setLong(2L); + Assert.assertEquals(1L, longCopy.getLong()); + + TsFloat floatValue = new TsFloat(1.0F); + TsFloat floatCopy = new TsFloat(); + floatCopy.copy(floatValue); + floatValue.setFloat(2.0F); + Assert.assertEquals(1.0F, floatCopy.getFloat(), 0.0F); + + TsDouble doubleValue = new TsDouble(1.0D); + TsDouble doubleCopy = new TsDouble(); + doubleCopy.copy(doubleValue); + doubleValue.setDouble(2.0D); + Assert.assertEquals(1.0D, doubleCopy.getDouble(), 0.0D); + + Binary binary = new Binary("original", TSFileConfig.STRING_CHARSET); + TsBinary binaryValue = new TsBinary(binary); + TsBinary binaryCopy = new TsBinary(); + binaryCopy.copy(binaryValue); + binary.getValues()[0] = 'O'; + Assert.assertEquals( + "original", binaryCopy.getBinary().getStringValue(TSFileConfig.STRING_CHARSET)); + + TsVector vectorValue = + new TsVector(new TsPrimitiveType[] {dateCopy, binaryCopy, null, new TsBoolean(true)}); + TsVector vectorCopy = new TsVector(); + vectorCopy.copy(vectorValue); + vectorValue.getVector()[0].setInt(0); + vectorValue.getVector()[1].getBinary().getValues()[0] = 'O'; + vectorValue.getVector()[3].setBoolean(false); + + Assert.assertNotSame(vectorValue.getVector(), vectorCopy.getVector()); + Assert.assertEquals(20260722, vectorCopy.getVector()[0].getInt()); + Assert.assertEquals(TSDataType.DATE, vectorCopy.getVector()[0].getDataType()); + Assert.assertEquals( + "original", + vectorCopy.getVector()[1].getBinary().getStringValue(TSFileConfig.STRING_CHARSET)); + Assert.assertNull(vectorCopy.getVector()[2]); + Assert.assertTrue(vectorCopy.getVector()[3].getBoolean()); + + booleanCopy.copy(null); + Assert.assertFalse(booleanCopy.getBoolean()); + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/utils/TypeCastTest.java b/java/tsfile/src/test/java/org/apache/tsfile/utils/TypeCastTest.java index 6398eac75..68384314d 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/utils/TypeCastTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/utils/TypeCastTest.java @@ -20,6 +20,8 @@ package org.apache.tsfile.utils; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.junit.Test; @@ -28,6 +30,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.function.IntFunction; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -36,6 +39,129 @@ public class TypeCastTest { + private static final TypeService CHECK_ARRAY_CAST_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> + (from, to, array) -> + assertArrayEquals( + (int[]) genValueArray(to), (int[]) to.castFromArray(from, array)); + case INT64, TIMESTAMP -> + (from, to, array) -> + assertArrayEquals( + (long[]) genValueArray(to), (long[]) to.castFromArray(from, array)); + case BOOLEAN -> + (from, to, array) -> + assertArrayEquals( + (boolean[]) genValueArray(to), (boolean[]) to.castFromArray(from, array)); + case BLOB, STRING, TEXT -> + (from, to, array) -> + assertArrayEquals( + TypeCastTest.EXPECTED_BINARY_ARRAY_SERVICE + .call(Type.fromTsDataType(from)) + .generate(array), + (Binary[]) to.castFromArray(from, array)); + case FLOAT -> + (from, to, array) -> + assertArrayEquals( + (float[]) genValueArray(to), (float[]) to.castFromArray(from, array), 0.1f); + case DOUBLE -> + (from, to, array) -> + assertArrayEquals( + (double[]) genValueArray(to), + (double[]) to.castFromArray(from, array), + 0.1); + case OBJECT, ROW, UNKNOWN, VECTOR -> + (from, to, array) -> fail("Unexpected type: " + to); + }; + + private static final TypeService EXPECTED_BINARY_ARRAY_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32 -> + array -> { + int[] values = (int[]) array; + return toBinaryArray(values.length, i -> String.valueOf(values[i])); + }; + case DATE -> + array -> { + int[] values = (int[]) array; + return toBinaryArray( + values.length, i -> TSDataType.getDateStringValue(values[i])); + }; + case INT64, TIMESTAMP -> + array -> { + long[] values = (long[]) array; + return toBinaryArray(values.length, i -> String.valueOf(values[i])); + }; + case FLOAT -> + array -> { + float[] values = (float[]) array; + return toBinaryArray(values.length, i -> String.valueOf(values[i])); + }; + case DOUBLE -> + array -> { + double[] values = (double[]) array; + return toBinaryArray(values.length, i -> String.valueOf(values[i])); + }; + case BOOLEAN -> + array -> { + boolean[] values = (boolean[]) array; + return toBinaryArray(values.length, i -> String.valueOf(values[i])); + }; + case BLOB, STRING, TEXT -> array -> (Binary[]) array; + case OBJECT, ROW, UNKNOWN, VECTOR -> + array -> { + throw new IllegalArgumentException( + "Unsupported data type: " + type.getTypeEnum()); + }; + }; + + private static final TypeService GENERATE_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> () -> 1; + case INT64, TIMESTAMP -> () -> 1L; + case BOOLEAN -> () -> false; + case FLOAT -> () -> 1.0f; + case DOUBLE -> () -> 1.0; + case BLOB, OBJECT, STRING, TEXT -> () -> new Binary("1", StandardCharsets.UTF_8); + case ROW, UNKNOWN, VECTOR -> + () -> { + throw new IllegalArgumentException( + "Unsupported data type: " + type.getTypeEnum()); + }; + }; + + private static final TypeService GENERATE_VALUE_ARRAY_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE -> () -> new int[] {1, 2, 3}; + case INT64, TIMESTAMP -> () -> new long[] {1, 2, 3}; + case BOOLEAN -> () -> new boolean[] {true, false}; + case FLOAT -> () -> new float[] {1.0f, 2.0f, 3.0f}; + case DOUBLE -> () -> new double[] {1.0, 2.0, 3.0}; + case BLOB, OBJECT, STRING, TEXT -> + () -> + new Binary[] { + new Binary("1", StandardCharsets.UTF_8), + new Binary("2", StandardCharsets.UTF_8), + new Binary("3", StandardCharsets.UTF_8) + }; + case ROW, UNKNOWN, VECTOR -> + () -> { + throw new IllegalArgumentException( + "Unsupported data type: " + type.getTypeEnum()); + }; + }; + + static { + CHECK_ARRAY_CAST_SERVICE.check(); + EXPECTED_BINARY_ARRAY_SERVICE.check(); + GENERATE_VALUE_SERVICE.check(); + GENERATE_VALUE_ARRAY_SERVICE.check(); + } + @Test public void testSingleCast() { Set dataTypes = new HashSet<>(); @@ -85,154 +211,42 @@ public void testArrayCast() { assertThrows(ClassCastException.class, () -> to.castFromArray(from, array)); continue; } - switch (to) { - case INT32: - case DATE: - assertArrayEquals((int[]) genValueArray(to), (int[]) to.castFromArray(from, array)); - break; - case INT64: - case TIMESTAMP: - assertArrayEquals((long[]) genValueArray(to), (long[]) to.castFromArray(from, array)); - break; - case BOOLEAN: - assertArrayEquals( - (boolean[]) genValueArray(to), (boolean[]) to.castFromArray(from, array)); - break; - case STRING: - case BLOB: - case TEXT: - switch (from) { - case BLOB: - case STRING: - assertArrayEquals((Binary[]) array, (Binary[]) to.castFromArray(from, array)); - break; - case INT32: - int[] tmpInt = (int[]) array; - Binary[] intResult = new Binary[tmpInt.length]; - for (int i = 0; i < tmpInt.length; i++) { - intResult[i] = new Binary(String.valueOf(tmpInt[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(intResult, (Binary[]) to.castFromArray(from, array)); - break; - case DATE: - int[] tmpDate = (int[]) array; - Binary[] dateResult = new Binary[tmpDate.length]; - for (int i = 0; i < tmpDate.length; i++) { - dateResult[i] = - new Binary(TSDataType.getDateStringValue(tmpDate[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(dateResult, (Binary[]) to.castFromArray(from, array)); - break; - case INT64: - case TIMESTAMP: - long[] tmpLong = (long[]) array; - Binary[] longResult = new Binary[tmpLong.length]; - for (int i = 0; i < tmpLong.length; i++) { - longResult[i] = new Binary(String.valueOf(tmpLong[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(longResult, (Binary[]) to.castFromArray(from, array)); - break; - case FLOAT: - float[] tmpFloat = (float[]) array; - Binary[] floatResult = new Binary[tmpFloat.length]; - for (int i = 0; i < tmpFloat.length; i++) { - floatResult[i] = new Binary(String.valueOf(tmpFloat[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(floatResult, (Binary[]) to.castFromArray(from, array)); - break; - case DOUBLE: - double[] tmpDouble = (double[]) array; - Binary[] doubleResult = new Binary[tmpDouble.length]; - for (int i = 0; i < tmpDouble.length; i++) { - doubleResult[i] = - new Binary(String.valueOf(tmpDouble[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(doubleResult, (Binary[]) to.castFromArray(from, array)); - break; - case BOOLEAN: - boolean[] tmpBoolean = (boolean[]) array; - Binary[] booleanResult = new Binary[tmpBoolean.length]; - for (int i = 0; i < tmpBoolean.length; i++) { - booleanResult[i] = - new Binary(String.valueOf(tmpBoolean[i]), StandardCharsets.UTF_8); - } - assertArrayEquals(booleanResult, (Binary[]) to.castFromArray(from, array)); - break; - default: - break; - } - break; - case FLOAT: - assertArrayEquals( - (float[]) genValueArray(to), (float[]) to.castFromArray(from, array), 0.1f); - break; - case DOUBLE: - assertArrayEquals( - (double[]) genValueArray(to), (double[]) to.castFromArray(from, array), 0.1); - break; - case UNKNOWN: - case VECTOR: - default: - fail("Unexpected type: " + to); - } + CHECK_ARRAY_CAST_SERVICE.call(Type.fromTsDataType(to)).check(from, to, array); } } } - private Object genValue(TSDataType dataType) { - int i = 1; - switch (dataType) { - case INT32: - case DATE: - return i; - case TIMESTAMP: - case INT64: - return (long) i; - case BOOLEAN: - return false; - case FLOAT: - return i * 1.0f; - case DOUBLE: - return i * 1.0; - case STRING: - case TEXT: - case BLOB: - case OBJECT: - return new Binary(Integer.toString(i), StandardCharsets.UTF_8); - case UNKNOWN: - case VECTOR: - default: - throw new IllegalArgumentException("Unsupported data type: " + dataType); - } + private static Object genValue(TSDataType dataType) { + return GENERATE_VALUE_SERVICE.call(Type.fromTsDataType(dataType)).generate(); + } + + private static Object genValueArray(TSDataType dataType) { + return GENERATE_VALUE_ARRAY_SERVICE.call(Type.fromTsDataType(dataType)).generate(); } - private Object genValueArray(TSDataType dataType) { - switch (dataType) { - case INT32: - case DATE: - return new int[] {1, 2, 3}; - case TIMESTAMP: - case INT64: - return new long[] {1, 2, 3}; - case BOOLEAN: - return new boolean[] {true, false}; - case FLOAT: - return new float[] {1.0f, 2.0f, 3.0f}; - case DOUBLE: - return new double[] {1.0, 2.0, 3.0}; - case STRING: - case TEXT: - case BLOB: - case OBJECT: - return new Binary[] { - new Binary(Integer.toString(1), StandardCharsets.UTF_8), - new Binary(Integer.toString(2), StandardCharsets.UTF_8), - new Binary(Integer.toString(3), StandardCharsets.UTF_8) - }; - case UNKNOWN: - case VECTOR: - default: - throw new IllegalArgumentException("Unsupported data type: " + dataType); + private static Binary[] toBinaryArray(int length, IntFunction valueProvider) { + Binary[] result = new Binary[length]; + for (int i = 0; i < length; i++) { + result[i] = new Binary(valueProvider.apply(i), StandardCharsets.UTF_8); } + return result; + } + + @FunctionalInterface + private interface ArrayCastChecker { + + void check(TSDataType from, TSDataType to, Object array); + } + + @FunctionalInterface + private interface BinaryArrayGenerator { + + Binary[] generate(Object array); + } + + @FunctionalInterface + private interface ValueGenerator { + + Object generate(); } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/write/record/TabletTest.java b/java/tsfile/src/test/java/org/apache/tsfile/write/record/TabletTest.java index ab4bf377b..660fe8534 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/write/record/TabletTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/write/record/TabletTest.java @@ -24,6 +24,8 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.read.common.type.Type; +import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BitMap; import org.apache.tsfile.utils.BytesUtils; @@ -59,6 +61,98 @@ public class TabletTest { + private static final TypeService SAMPLE_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> (row, col, variableBinaryLength) -> (row + col) % 2 == 0; + case INT32 -> (row, col, variableBinaryLength) -> row + col * 100; + case INT64, TIMESTAMP -> + (row, col, variableBinaryLength) -> (long) (valueOffset(row, col) * 1_000_000L); + case FLOAT -> (row, col, variableBinaryLength) -> (row + col) * 1.5f; + case DOUBLE -> (row, col, variableBinaryLength) -> (row + col) * 2.5; + case TEXT, STRING -> + (row, col, variableBinaryLength) -> + stringOfLength(binaryPayloadLength(variableBinaryLength, row, col)); + case BLOB -> + (row, col, variableBinaryLength) -> + binaryOfLength(binaryPayloadLength(variableBinaryLength, row, col)); + case DATE -> + (row, col, variableBinaryLength) -> + LocalDate.of(2000 + (row % 20), (col % 12) + 1, (row % 28) + 1); + case OBJECT, ROW, UNKNOWN, VECTOR -> + (row, col, variableBinaryLength) -> { + throw new IllegalArgumentException( + "Unsupported type in test: " + type.getTypeEnum()); + }; + }; + + private static final TypeService WRITE_TABLET_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32 -> (tablet, row, col, value) -> tablet.addValue(row, col, value); + case INT64, TIMESTAMP -> + (tablet, row, col, value) -> tablet.addValue(row, col, (long) value); + case FLOAT -> (tablet, row, col, value) -> tablet.addValue(row, col, value * 1.0f); + case DOUBLE -> (tablet, row, col, value) -> tablet.addValue(row, col, value * 1.0); + case BOOLEAN -> (tablet, row, col, value) -> tablet.addValue(row, col, value % 2 == 0); + case BLOB, STRING, TEXT -> + (tablet, row, col, value) -> tablet.addValue(row, col, String.valueOf(value)); + case OBJECT -> + (tablet, row, col, value) -> + tablet.addValue(row, col, value % 2 == 0, value, new byte[] {(byte) row}); + case DATE -> + (tablet, row, col, value) -> tablet.addValue(row, col, LocalDate.of(value, 1, 1)); + case ROW, UNKNOWN, VECTOR -> + (tablet, row, col, value) -> { + throw new IllegalArgumentException( + "Unsupported type in test: " + type.getTypeEnum()); + }; + }; + + private static final TypeService CHECK_TABLET_VALUE_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32 -> (tablet, row, col) -> assertEquals(row, tablet.getValue(row, col)); + case INT64, TIMESTAMP -> + (tablet, row, col) -> assertEquals((long) row, tablet.getValue(row, col)); + case FLOAT -> + (tablet, row, col) -> + assertEquals(row * 1.0f, (float) tablet.getValue(row, col), 0.0001f); + case DOUBLE -> + (tablet, row, col) -> + assertEquals(row * 1.0, (double) tablet.getValue(row, col), 0.0001); + case BOOLEAN -> + (tablet, row, col) -> assertEquals(row % 2 == 0, tablet.getValue(row, col)); + case BLOB, STRING, TEXT -> + (tablet, row, col) -> + assertEquals( + new Binary(String.valueOf(row).getBytes(StandardCharsets.UTF_8)), + tablet.getValue(row, col)); + case OBJECT -> + (tablet, row, col) -> { + byte[] content = new byte[] {(byte) row}; + byte[] expected = new byte[content.length + 9]; + expected[0] = (byte) (row % 2); + System.arraycopy(BytesUtils.longToBytes(row), 0, expected, 1, 8); + System.arraycopy(content, 0, expected, 9, content.length); + assertEquals(new Binary(expected), tablet.getValue(row, col)); + }; + case DATE -> + (tablet, row, col) -> + assertEquals(LocalDate.of(row, 1, 1), tablet.getValue(row, col)); + case ROW, UNKNOWN, VECTOR -> + (tablet, row, col) -> { + throw new IllegalArgumentException( + "Unsupported type in test: " + type.getTypeEnum()); + }; + }; + + static { + SAMPLE_VALUE_SERVICE.check(); + WRITE_TABLET_VALUE_SERVICE.check(); + CHECK_TABLET_VALUE_SERVICE.check(); + } + @Test public void testAddValue() { Tablet tablet = @@ -694,28 +788,9 @@ private static int binaryPayloadLength(boolean variableBinaryLength, int row, in } private Object sampleValue(TSDataType type, int row, int col, boolean variableBinaryLength) { - switch (type) { - case BOOLEAN: - return (row + col) % 2 == 0; - case INT32: - return row + col * 100; - case INT64: - case TIMESTAMP: - return (long) (valueOffset(row, col) * 1_000_000L); - case FLOAT: - return (row + col) * 1.5f; - case DOUBLE: - return (row + col) * 2.5; - case TEXT: - case STRING: - return stringOfLength(binaryPayloadLength(variableBinaryLength, row, col)); - case BLOB: - return binaryOfLength(binaryPayloadLength(variableBinaryLength, row, col)); - case DATE: - return LocalDate.of(2000 + (row % 20), (col % 12) + 1, (row % 28) + 1); - default: - throw new IllegalArgumentException("Unsupported type in test: " + type); - } + return SAMPLE_VALUE_SERVICE + .call(Type.fromTsDataType(type)) + .generate(row, col, variableBinaryLength); } private static int valueOffset(int row, int col) { @@ -799,35 +874,9 @@ private void fillTablet(Tablet t, int valueOffset, int length) { for (int i = 0; i < length; i++) { t.addTimestamp(i, i + valueOffset); for (int j = 0; j < t.getSchemas().size(); j++) { - switch (t.getSchemas().get(j).getType()) { - case INT32: - t.addValue(i, j, i + valueOffset); - break; - case TIMESTAMP: - case INT64: - t.addValue(i, j, (long) (i + valueOffset)); - break; - case FLOAT: - t.addValue(i, j, (i + valueOffset) * 1.0f); - break; - case DOUBLE: - t.addValue(i, j, (i + valueOffset) * 1.0); - break; - case BOOLEAN: - t.addValue(i, j, (i + valueOffset) % 2 == 0); - break; - case TEXT: - case STRING: - case BLOB: - t.addValue(i, j, String.valueOf(i + valueOffset)); - break; - case OBJECT: - t.addValue(i, j, (i + valueOffset) % 2 == 0, i + valueOffset, new byte[] {(byte) i}); - break; - case DATE: - t.addValue(i, j, LocalDate.of(i + valueOffset, 1, 1)); - break; - } + WRITE_TABLET_VALUE_SERVICE + .call(Type.fromTsDataType(t.getSchemas().get(j).getType())) + .write(t, i, j, i + valueOffset); } } } @@ -1030,45 +1079,28 @@ private void checkAppendedTablet( } assertFalse(result.isNull(i, j)); - switch (result.getSchemas().get(j).getType()) { - case INT32: - assertEquals(i, result.getValue(i, j)); - break; - case TIMESTAMP: - case INT64: - assertEquals((long) i, result.getValue(i, j)); - break; - case FLOAT: - assertEquals(i * 1.0f, (float) result.getValue(i, j), 0.0001f); - break; - case DOUBLE: - assertEquals(i * 1.0, (double) result.getValue(i, j), 0.0001); - break; - case BOOLEAN: - assertEquals(i % 2 == 0, result.getValue(i, j)); - break; - case TEXT: - case BLOB: - case STRING: - assertEquals( - new Binary(String.valueOf(i).getBytes(StandardCharsets.UTF_8)), - result.getValue(i, j)); - break; - case OBJECT: - { - byte[] content = new byte[] {(byte) i}; - byte[] expected = new byte[content.length + 9]; - expected[0] = (byte) (i % 2); - System.arraycopy(BytesUtils.longToBytes(i), 0, expected, 1, 8); - System.arraycopy(content, 0, expected, 9, content.length); - assertEquals(new Binary(expected), result.getValue(i, j)); - } - break; - case DATE: - assertEquals(LocalDate.of(i, 1, 1), result.getValue(i, j)); - break; - } + CHECK_TABLET_VALUE_SERVICE + .call(Type.fromTsDataType(result.getSchemas().get(j).getType())) + .check(result, i, j); } } } + + @FunctionalInterface + private interface SampleValueGenerator { + + Object generate(int row, int col, boolean variableBinaryLength); + } + + @FunctionalInterface + private interface TabletValueWriter { + + void write(Tablet tablet, int row, int col, int value); + } + + @FunctionalInterface + private interface TabletValueChecker { + + void check(Tablet tablet, int row, int col); + } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/write/writer/AlignedChunkWriterImplTest.java b/java/tsfile/src/test/java/org/apache/tsfile/write/writer/AlignedChunkWriterImplTest.java index 749141235..5a8dd5a99 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/write/writer/AlignedChunkWriterImplTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/write/writer/AlignedChunkWriterImplTest.java @@ -22,6 +22,9 @@ import org.apache.tsfile.file.MetaMarker; import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.TimeColumn; import org.apache.tsfile.utils.PublicBAOS; import org.apache.tsfile.utils.ReadWriteForEncodingUtils; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -31,12 +34,37 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Arrays; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class AlignedChunkWriterImplTest { + @Test + public void testBatchWrite() { + AlignedChunkWriterImpl chunkWriter = + new AlignedChunkWriterImpl(new VectorMeasurementSchemaStub()); + TsBlockBuilder builder = + new TsBlockBuilder(Arrays.asList(TSDataType.FLOAT, TSDataType.INT32, TSDataType.DOUBLE)); + for (int time = 1; time <= 20; time++) { + builder.getTimeColumnBuilder().writeLong(time); + builder.getValueColumnBuilders()[0].writeFloat(time); + builder.getValueColumnBuilders()[1].writeInt(time); + builder.getValueColumnBuilders()[2].writeDouble(time); + builder.declarePosition(); + } + TsBlock tsBlock = builder.build(); + + chunkWriter.write( + (TimeColumn) tsBlock.getTimeColumn(), + tsBlock.getValueColumns(), + tsBlock.getPositionCount()); + chunkWriter.sealCurrentPage(); + + assertEquals(485, chunkWriter.getSerializedChunkSize()); + } + @Test public void testWrite1() { VectorMeasurementSchemaStub measurementSchema = new VectorMeasurementSchemaStub(); diff --git a/pom.xml b/pom.xml index eb9380fb9..68eef8e04 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 17 17 17 - + false 3.30.2-b1 2.44.3 @@ -263,7 +263,7 @@ validate - +