This is an automated email from the ASF dual-hosted git repository. haonan pushed a commit to branch rc/2.2.1 in repository https://gitbox.apache.org/repos/asf/tsfile.git
commit add5b3b3a9944839d8567f0a62a7a392b39c6dd2 Author: libo <[email protected]> AuthorDate: Wed Sep 3 11:48:38 2025 +0800 Alter column other type to string type (#588) * Resolve may happen the problem that type conversion. * Format code. * Fix code. * Fix code. * Modify functions related type transfer in every operators class via template * Support for what other type transfer to text/string type. * Fix * Allow modify value of date type, format to LocalDate result. * Resolve the problem that BinaryColumnBuilder wrong use string of int type to transfer string of date type. * Resolve the problem that the type of statistics don't match * Fix * Fix bug. * Format code. * Fix IT test. * Fix IT bug. * Adjust some problems. * Format code. --- .../java/org/apache/tsfile/enums/TSDataType.java | 98 +++++- .../codegen/templates/FilterOperatorsTemplate.ftl | 356 +++++++++++++++++++-- .../file/metadata/statistics/Statistics.java | 51 ++- .../common/block/column/BinaryColumnBuilder.java | 30 ++ .../org/apache/tsfile/read/reader/IPageReader.java | 2 + .../reader/page/AbstractAlignedPageReader.java | 5 + .../apache/tsfile/read/reader/page/PageReader.java | 5 + .../tsfile/read/reader/page/ValuePageReader.java | 41 ++- .../java/org/apache/tsfile/utils/TypeCastTest.java | 81 ++++- 9 files changed, 628 insertions(+), 41 deletions(-) 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 cf17aa04..492d1bab 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 @@ -19,6 +19,7 @@ package org.apache.tsfile.enums; +import org.apache.tsfile.utils.Binary; import org.apache.tsfile.write.UnSupportedDataTypeException; import java.io.DataOutputStream; @@ -26,6 +27,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -99,6 +101,14 @@ public enum TSDataType { Set<TSDataType> textCompatibleTypes = new HashSet<>(); textCompatibleTypes.add(STRING); + textCompatibleTypes.add(INT32); + textCompatibleTypes.add(INT64); + textCompatibleTypes.add(FLOAT); + textCompatibleTypes.add(DOUBLE); + textCompatibleTypes.add(BOOLEAN); + textCompatibleTypes.add(BLOB); + textCompatibleTypes.add(DATE); + textCompatibleTypes.add(TIMESTAMP); compatibleTypes.put(TEXT, textCompatibleTypes); compatibleTypes.put(VECTOR, Collections.emptySet()); @@ -119,6 +129,15 @@ public enum TSDataType { Set<TSDataType> stringCompatibleTypes = new HashSet<>(); stringCompatibleTypes.add(TEXT); + // add + stringCompatibleTypes.add(INT32); + stringCompatibleTypes.add(INT64); + stringCompatibleTypes.add(FLOAT); + stringCompatibleTypes.add(DOUBLE); + stringCompatibleTypes.add(BOOLEAN); + stringCompatibleTypes.add(BLOB); + stringCompatibleTypes.add(DATE); + stringCompatibleTypes.add(TIMESTAMP); compatibleTypes.put(STRING, stringCompatibleTypes); } @@ -232,6 +251,17 @@ public enum TSDataType { 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; } @@ -262,6 +292,17 @@ public enum TSDataType { 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; } @@ -331,12 +372,6 @@ public enum TSDataType { } else { break; } - case TEXT: - if (sourceType == TSDataType.TEXT || sourceType == STRING) { - return array; - } else { - break; - } case TIMESTAMP: if (sourceType == TSDataType.TIMESTAMP) { return array; @@ -361,9 +396,54 @@ public enum TSDataType { } else { break; } + case TEXT: case STRING: - if (sourceType == TSDataType.STRING || sourceType == TSDataType.TEXT) { + 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; } @@ -487,4 +567,8 @@ public enum TSDataType { public boolean isBinary() { return this == TEXT || this == STRING || this == BLOB; } + + public static String getDateStringValue(int value) { + return String.format("%04d-%02d-%02d", value / 10000, (value % 10000) / 100, value % 100); + } } diff --git a/java/tsfile/src/main/codegen/templates/FilterOperatorsTemplate.ftl b/java/tsfile/src/main/codegen/templates/FilterOperatorsTemplate.ftl index 0fe475d1..7e39b361 100644 --- a/java/tsfile/src/main/codegen/templates/FilterOperatorsTemplate.ftl +++ b/java/tsfile/src/main/codegen/templates/FilterOperatorsTemplate.ftl @@ -42,6 +42,9 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; +<#if filter.dataType == "Binary"> +import java.nio.charset.StandardCharsets; +</#if> <#if filter.dataType != "boolean"> import java.util.Collections; </#if> @@ -137,8 +140,15 @@ public final class ${className} { @Override public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } + else{ + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -156,7 +166,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -166,6 +176,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) < 0 + || constant.compareTo((${filter.dataType}) statistics.getMaxValue()) > 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) < 0 + || constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) > 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -179,7 +205,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -189,6 +215,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) == 0 + && constant.compareTo((${filter.dataType}) statistics.getMaxValue()) == 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) == 0 + && constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) == 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -222,8 +264,14 @@ public final class ${className} { @Override public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -241,7 +289,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -251,6 +299,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) == 0 + && constant.compareTo((${filter.dataType}) statistics.getMaxValue()) == 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) == 0 + && constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) == 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -265,7 +329,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -275,6 +339,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) < 0 + || constant.compareTo((${filter.dataType}) statistics.getMaxValue()) > 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) < 0 + || constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) > 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -308,8 +388,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -329,7 +415,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -338,6 +424,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMinValue() instanceof Binary) { + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) <= 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) <= 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -351,7 +451,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -360,6 +460,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMaxValue() instanceof Binary){ + return constant.compareTo((${filter.dataType}) statistics.getMaxValue()) > 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) > 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -392,8 +506,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -413,7 +533,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -422,6 +542,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMinValue() instanceof Binary) { + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) < 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) < 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -435,7 +569,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -444,6 +578,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMaxValue() instanceof Binary){ + return constant.compareTo((${filter.dataType}) statistics.getMaxValue()) >= 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) >= 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -476,8 +624,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -497,7 +651,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -506,6 +660,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMaxValue() instanceof Binary) { + return constant.compareTo((${filter.dataType}) statistics.getMaxValue()) >= 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) >= 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -519,7 +687,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -528,6 +696,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMinValue() instanceof Binary){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) < 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) < 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -560,8 +742,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -581,7 +769,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -590,6 +778,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMaxValue() instanceof Binary) { + return constant.compareTo((${filter.dataType}) statistics.getMaxValue()) > 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)) > 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -603,7 +805,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -612,6 +814,20 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if(statistics.getMinValue() instanceof Binary){ + return constant.compareTo((${filter.dataType}) statistics.getMinValue()) <= 0; + } + else{ + return constant.compareTo(new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)) <= 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -712,8 +928,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -735,7 +957,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -745,6 +967,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMaxValue() instanceof Binary) && (statistics.getMinValue() instanceof Binary)) { + return ((${filter.dataType}) statistics.getMaxValue()).compareTo(min) < 0 + || ((${filter.dataType}) statistics.getMinValue()).compareTo(max) > 0; + } + else{ + return (new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)).compareTo(min) < 0 + || (new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)).compareTo(max) > 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -758,7 +996,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -768,6 +1006,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return ((${filter.dataType}) statistics.getMinValue()).compareTo(min) >= 0 + && ((${filter.dataType}) statistics.getMaxValue()).compareTo(max) <= 0; + } + else{ + return (new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)).compareTo(min) >= 0 + && (new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)).compareTo(max) <= 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -801,8 +1055,14 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return valueSatisfy((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return valueSatisfy((${filter.dataType}) value); + } else { + return valueSatisfy(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return valueSatisfy(((Number) value).${filter.dataType}Value()); </#if> @@ -823,7 +1083,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean canSkip(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -833,6 +1093,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)) { + return ((${filter.dataType}) statistics.getMinValue()).compareTo(min) >= 0 + && ((${filter.dataType}) statistics.getMaxValue()).compareTo(max) <= 0; + } + else{ + return (new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)).compareTo(min) >= 0 + && (new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)).compareTo(max) <= 0; + } + <#else> + return false; + </#if> <#else> // drop if value < min || value > max if(statistics.isEmpty()){ @@ -846,7 +1122,7 @@ public final class ${className} { @Override @SuppressWarnings("unchecked") public boolean allSatisfy(Statistics<? extends Serializable> statistics) { - <#if filter.dataType == "boolean" || filter.dataType == "Binary"> + <#if filter.dataType == "boolean"> <#if filter.javaBoxName == "String"> if(statistics.isEmpty()){ return false; @@ -856,6 +1132,22 @@ public final class ${className} { <#else> return false; </#if> + <#elseif filter.dataType == "Binary"> + <#if filter.javaBoxName == "String"> + if(statistics.isEmpty()){ + return false; + } + if((statistics.getMinValue() instanceof Binary) && (statistics.getMaxValue() instanceof Binary)){ + return ((${filter.dataType}) statistics.getMinValue()).compareTo(max) > 0 + || ((${filter.dataType}) statistics.getMaxValue()).compareTo(min) < 0; + } + else{ + return (new ${filter.dataType}(String.valueOf(statistics.getMinValue()), StandardCharsets.UTF_8)).compareTo(max) > 0 + || (new ${filter.dataType}(String.valueOf(statistics.getMaxValue()), StandardCharsets.UTF_8)).compareTo(min) < 0; + } + <#else> + return false; + </#if> <#else> if(statistics.isEmpty()){ return false; @@ -994,8 +1286,14 @@ public final class ${className} { @Override public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return candidates.contains((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return candidates.contains((${filter.dataType}) value); + } else { + return candidates.contains(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return candidates.contains(((Number) value).${filter.dataType}Value()); </#if> @@ -1142,8 +1440,14 @@ public final class ${className} { @Override public boolean valueSatisfy(Object value){ - <#if filter.dataType == "boolean" || filter.dataType == "Binary" || filter.javaBoxName == "String"> + <#if filter.dataType == "boolean" || filter.javaBoxName == "String"> return !candidates.contains((${filter.dataType}) value); + <#elseif filter.dataType == "Binary"> + if(value instanceof Binary){ + return !candidates.contains((${filter.dataType}) value); + } else { + return !candidates.contains(new ${filter.dataType}(String.valueOf(value), StandardCharsets.UTF_8)); + } <#else> return !candidates.contains(((Number) value).${filter.dataType}Value()); </#if> 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 ee41b063..b1c2c463 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 @@ -24,6 +24,7 @@ import org.apache.tsfile.exception.filter.StatisticsClassException; import org.apache.tsfile.exception.write.UnknownColumnTypeException; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.ReadWriteForEncodingUtils; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -35,7 +36,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.nio.ByteBuffer; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; /** * This class is used for recording statistic information of each measurement in a delta file. While @@ -63,6 +66,35 @@ public abstract class Statistics<T extends Serializable> { static final String STATS_UNSUPPORTED_MSG = "%s statistics does not support: %s"; + private static final Set<Pair<TSDataType, TSDataType>> CAN_NOT_MERGE_PAIRS; + + static { + CAN_NOT_MERGE_PAIRS = new HashSet<>(); + + // related pair about STRING + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.INT32, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.INT64, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.FLOAT, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.DOUBLE, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.BOOLEAN, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.TIMESTAMP, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.DATE, TSDataType.STRING); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.BLOB, TSDataType.STRING); + + // related pair about TEXT + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.INT32, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.INT64, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.FLOAT, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.DOUBLE, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.BOOLEAN, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.TIMESTAMP, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.DATE, TSDataType.TEXT); + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.BLOB, TSDataType.TEXT); + + // related pari about TEXT and STRING + addSymmetricPairs(CAN_NOT_MERGE_PAIRS, TSDataType.TEXT, TSDataType.STRING); + } + /** * static method providing statistic instance for respective data type. * @@ -229,7 +261,24 @@ public abstract class Statistics<T extends Serializable> { return to.isCompatible(from) && // cannot alter from TEXT to STRING because we cannot add statistic to the existing chunks - !(from == TSDataType.TEXT && to == TSDataType.STRING); + isSatisfyMerge(from, to); + } + + private static void addSymmetricPairs( + Set<Pair<TSDataType, TSDataType>> set, TSDataType... dataTypes) { + for (int i = 0; i < dataTypes.length; i++) { + for (int j = i + 1; j < dataTypes.length; j++) { + set.add(new Pair<>(dataTypes[i], dataTypes[j])); + set.add(new Pair<>(dataTypes[j], dataTypes[i])); + } + } + } + + public static boolean isSatisfyMerge(TSDataType from, TSDataType to) { + if (from == to) { + return true; + } + return !CAN_NOT_MERGE_PAIRS.contains(new Pair<>(from, to)); } public void update(long time, boolean value) { diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumnBuilder.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumnBuilder.java index be952eaa..d6facd48 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumnBuilder.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/BinaryColumnBuilder.java @@ -28,6 +28,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.UnSupportedDataTypeException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import static java.lang.Math.max; @@ -100,6 +101,35 @@ public class BinaryColumnBuilder implements ColumnBuilder { return writeBinary(column.getBinary(index)); } + @Override + public ColumnBuilder writeBoolean(boolean value) { + return writeBinary(new Binary(String.valueOf(value), StandardCharsets.UTF_8)); + } + + @Override + public ColumnBuilder writeInt(int value) { + return writeBinary(new Binary(String.valueOf(value), StandardCharsets.UTF_8)); + } + + @Override + public ColumnBuilder writeLong(long value) { + return writeBinary(new Binary(String.valueOf(value), StandardCharsets.UTF_8)); + } + + @Override + public ColumnBuilder writeFloat(float value) { + return writeBinary(new Binary(String.valueOf(value), StandardCharsets.UTF_8)); + } + + @Override + public ColumnBuilder writeDouble(double value) { + return writeBinary(new Binary(String.valueOf(value), StandardCharsets.UTF_8)); + } + + public ColumnBuilder writeDate(int value) { + return writeBinary(new Binary(TSDataType.getDateStringValue(value), StandardCharsets.UTF_8)); + } + @Override public ColumnBuilder writeTsPrimitiveType(TsPrimitiveType value) { return writeBinary(value.getBinary()); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/IPageReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/IPageReader.java index 9d6980be..1b870f86 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/IPageReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/IPageReader.java @@ -43,6 +43,8 @@ public interface IPageReader extends IMetadata { boolean isModified(); + void setModified(boolean modified); + void initTsBlockBuilder(List<TSDataType> dataTypes); void setLimitOffset(PaginationController paginationController); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/AbstractAlignedPageReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/AbstractAlignedPageReader.java index 9230d486..ad5af3b5 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/AbstractAlignedPageReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/reader/page/AbstractAlignedPageReader.java @@ -348,6 +348,11 @@ public abstract class AbstractAlignedPageReader implements IPageReader { return isModified; } + @Override + public void setModified(boolean modified) { + this.isModified = modified; + } + @Override public void initTsBlockBuilder(List<TSDataType> dataTypes) { if (paginationController.hasLimit()) { 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 ee74f815..7ba234cf 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 @@ -413,6 +413,11 @@ public class PageReader implements IPageReader { return pageHeader.isModified(); } + @Override + public void setModified(boolean modified) { + pageHeader.setModified(modified); + } + @Override public void initTsBlockBuilder(List<TSDataType> dataTypes) { // do nothing 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 fc9dd375..998b2117 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,6 +27,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; 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.filter.basic.Filter; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -318,7 +319,6 @@ public class ValuePageReader { } break; case INT32: - case DATE: int anInt = valueDecoder.readInt(valueBuffer); if (keepCurrentRow[i]) { if (isDeleted[i]) { @@ -328,6 +328,20 @@ public class ValuePageReader { } } 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); @@ -403,12 +417,21 @@ public class ValuePageReader { } break; case INT32: - case DATE: 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); @@ -485,7 +508,15 @@ public class ValuePageReader { continue; } int aInt = valueDecoder.readInt(valueBuffer); - columnBuilder.writeInt(aInt); + if (dataType == TSDataType.INT32) { + columnBuilder.writeInt(aInt); + } else { + if (columnBuilder instanceof BinaryColumnBuilder) { + ((BinaryColumnBuilder) columnBuilder).writeDate(aInt); + } else { + columnBuilder.writeInt(aInt); + } + } } break; case INT64: @@ -584,6 +615,10 @@ public class ValuePageReader { return pageHeader.isModified(); } + public void setModified(boolean modified) { + pageHeader.setModified(modified); + } + public boolean isDeleted(long timestamp) { while (deleteIntervalList != null && deleteCursor < deleteIntervalList.size()) { if (deleteIntervalList.get(deleteCursor).contains(timestamp)) { 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 10d26db1..4d1d0006 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 @@ -24,6 +24,7 @@ import org.apache.tsfile.enums.TSDataType; import org.junit.Test; import java.nio.charset.StandardCharsets; +import java.time.LocalDate; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -46,7 +47,21 @@ public class TypeCastTest { for (TSDataType to : dataTypes) { Object src = genValue(from); if (to.isCompatible(from)) { - assertEquals(genValue(to), to.castFromSingleValue(from, src)); + if (to == TSDataType.STRING || to == TSDataType.TEXT) { + if (from == TSDataType.DATE) { + assertEquals( + new Binary(LocalDate.ofEpochDay((int) src).toString(), StandardCharsets.UTF_8), + new Binary( + LocalDate.ofEpochDay(Long.parseLong(genValue(to).toString())).toString(), + StandardCharsets.UTF_8)); + } else { + assertEquals( + new Binary(src.toString(), StandardCharsets.UTF_8), + to.castFromSingleValue(from, src)); + } + } else { + assertEquals(genValue(to), to.castFromSingleValue(from, src)); + } } else { assertThrows(ClassCastException.class, () -> to.castFromSingleValue(from, src)); } @@ -66,7 +81,7 @@ public class TypeCastTest { Object array = genValueArray(from); if (!to.isCompatible(from)) { assertThrows(ClassCastException.class, () -> to.castFromArray(from, array)); - return; + continue; } switch (to) { case INT32: @@ -84,8 +99,66 @@ public class TypeCastTest { case STRING: case BLOB: case TEXT: - assertArrayEquals( - (Binary[]) genValueArray(to), (Binary[]) to.castFromArray(from, array)); + 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(
