This is an automated email from the ASF dual-hosted git repository. jt2594838 pushed a commit to branch remove_swtich_type in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 9e58ae029a2c10e4ace4ccd12e1559c49c932488 Author: Tian Jiang <[email protected]> AuthorDate: Thu Aug 13 18:36:21 2026 +0800 multiple refactors --- .../org/apache/iotdb/calc/i18n/CalcMessages.java | 2 + .../org/apache/iotdb/calc/i18n/CalcMessages.java | 2 + .../aggregation/CentralMomentAccumulator.java | 36 +++---- .../aggregation/TableCentralMomentAccumulator.java | 38 +++---- .../grouped/GroupedCentralMomentAccumulator.java | 38 +++---- .../org/apache/iotdb/calc/utils/TypeServices.java | 36 ++++++- .../apache/iotdb/calc/utils/TypeServicesTest.java | 98 ++++++++++++++++++ .../apache/iotdb/db/i18n/DataNodePipeMessages.java | 6 ++ .../apache/iotdb/db/i18n/DataNodePipeMessages.java | 6 ++ .../iotdb/db/pipe/event/common/row/PipeRow.java | 28 +---- .../org/apache/iotdb/db/utils/TypeServices.java | 30 +++++- .../db/pipe/event/common/row/PipeRowTest.java | 115 +++++++++++++++++++++ 12 files changed, 340 insertions(+), 95 deletions(-) diff --git a/iotdb-core/calc-commons/src/main/i18n/en/org/apache/iotdb/calc/i18n/CalcMessages.java b/iotdb-core/calc-commons/src/main/i18n/en/org/apache/iotdb/calc/i18n/CalcMessages.java index 3a8a2afb8f2..c5c339d6df5 100644 --- a/iotdb-core/calc-commons/src/main/i18n/en/org/apache/iotdb/calc/i18n/CalcMessages.java +++ b/iotdb-core/calc-commons/src/main/i18n/en/org/apache/iotdb/calc/i18n/CalcMessages.java @@ -133,6 +133,8 @@ public final class CalcMessages { public static final String UNSUPPORTED_COMPARISON_OPERATOR = "Unsupported comparison operator: "; public static final String UNSUPPORTED_DATA_TYPE = "Unsupported data type: "; + public static final String UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION = + "Unsupported data type in CentralMoment Aggregation: %s"; public static final String UNSUPPORTED_DEFAULT_VALUE_DATA_TYPE_IN_LAG = "Unsupported default value's data type in Lag: "; public static final String UNSUPPORTED_DATA_TYPE_LOWER = "unsupported data type: "; diff --git a/iotdb-core/calc-commons/src/main/i18n/zh/org/apache/iotdb/calc/i18n/CalcMessages.java b/iotdb-core/calc-commons/src/main/i18n/zh/org/apache/iotdb/calc/i18n/CalcMessages.java index f3c3aa34f87..05a8e667fbf 100644 --- a/iotdb-core/calc-commons/src/main/i18n/zh/org/apache/iotdb/calc/i18n/CalcMessages.java +++ b/iotdb-core/calc-commons/src/main/i18n/zh/org/apache/iotdb/calc/i18n/CalcMessages.java @@ -126,6 +126,8 @@ public final class CalcMessages { public static final String UNSUPPORTED_COLUMN_TRANSFORMER = "不支持的 ColumnTransformer"; public static final String UNSUPPORTED_COMPARISON_OPERATOR = "不支持的比较运算符:"; public static final String UNSUPPORTED_DATA_TYPE = "不支持的数据类型:"; + public static final String UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION = + "CentralMoment 聚合中不支持的数据类型:%s"; public static final String UNSUPPORTED_DEFAULT_VALUE_DATA_TYPE_IN_LAG = "Lag 中不支持的默认值数据类型:"; public static final String UNSUPPORTED_DATA_TYPE_LOWER = "不支持的数据类型:"; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java index f3ae6ed6e69..ddd381cfc9a 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java @@ -14,10 +14,14 @@ package org.apache.iotdb.calc.execution.aggregation; +import org.apache.iotdb.calc.i18n.CalcMessages; +import org.apache.iotdb.calc.utils.TypeServices; + import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BitMap; @@ -35,7 +39,7 @@ public class CentralMomentAccumulator implements Accumulator { KURTOSIS } - private final TSDataType seriesDataType; + private final TypeServices.ColumnToDoubleConverter doubleValueConverter; private final MomentType momentType; private long count; @@ -45,7 +49,15 @@ public class CentralMomentAccumulator implements Accumulator { private double m4; public CentralMomentAccumulator(TSDataType seriesDataType, MomentType momentType) { - this.seriesDataType = seriesDataType; + this.doubleValueConverter = + TypeServices.NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE + .call(Type.fromTsDataType(seriesDataType)) + .create( + () -> + new UnsupportedOperationException( + String.format( + CalcMessages.UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION, + seriesDataType))); this.momentType = momentType; } @@ -60,25 +72,7 @@ public class CentralMomentAccumulator implements Accumulator { if (columns[1].isNull(i)) { continue; } - update(getDoubleValue(columns[1], i)); - } - } - - private double getDoubleValue(Column column, int position) { - switch (seriesDataType) { - case INT32: - case DATE: - return column.getInt(position); - case INT64: - case TIMESTAMP: - return column.getLong(position); - case FLOAT: - return column.getFloat(position); - case DOUBLE: - return column.getDouble(position); - default: - throw new UnsupportedOperationException( - "Unsupported data type in CentralMoment Aggregation: " + seriesDataType); + update(doubleValueConverter.convert(columns[1], i)); } } diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java index e3a7b629dcb..97a095c1838 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java @@ -15,6 +15,8 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation; import org.apache.iotdb.calc.execution.aggregation.CentralMomentAccumulator; +import org.apache.iotdb.calc.i18n.CalcMessages; +import org.apache.iotdb.calc.utils.TypeServices; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; @@ -23,6 +25,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; 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.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; @@ -39,6 +42,7 @@ public class TableCentralMomentAccumulator implements TableAccumulator { private static final double EPSILON = 1e-12; private final TSDataType seriesDataType; + private final TypeServices.ColumnToDoubleConverter doubleValueConverter; private final CentralMomentAccumulator.MomentType momentType; private long count; @@ -50,6 +54,15 @@ public class TableCentralMomentAccumulator implements TableAccumulator { public TableCentralMomentAccumulator( TSDataType seriesDataType, CentralMomentAccumulator.MomentType momentType) { this.seriesDataType = seriesDataType; + this.doubleValueConverter = + TypeServices.NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE + .call(Type.fromTsDataType(seriesDataType)) + .create( + () -> + new UnSupportedDataTypeException( + String.format( + CalcMessages.UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION, + seriesDataType))); this.momentType = momentType; } @@ -59,7 +72,7 @@ public class TableCentralMomentAccumulator implements TableAccumulator { if (mask.isSelectAll()) { for (int i = 0; i < positionCount; i++) { if (!arguments[0].isNull(i)) { - update(getDoubleValue(arguments[0], i)); + update(doubleValueConverter.convert(arguments[0], i)); } } } else { @@ -67,31 +80,12 @@ public class TableCentralMomentAccumulator implements TableAccumulator { for (int i = 0; i < positionCount; i++) { int position = selectedPositions[i]; if (!arguments[0].isNull(position)) { - update(getDoubleValue(arguments[0], position)); + update(doubleValueConverter.convert(arguments[0], position)); } } } } - private double getDoubleValue(Column column, int position) { - switch (seriesDataType) { - case INT32: - case DATE: - return column.getInt(position); - case INT64: - case TIMESTAMP: - return column.getLong(position); - case FLOAT: - return column.getFloat(position); - case DOUBLE: - return column.getDouble(position); - default: - throw new UnSupportedDataTypeException( - String.format( - "Unsupported data type in CentralMoment Aggregation: %s", seriesDataType)); - } - } - private void update(double value) { long n1 = count; long n = n1 + 1; @@ -236,7 +230,7 @@ public class TableCentralMomentAccumulator implements TableAccumulator { return; } - double value = getDoubleValue(arguments[0], 0); + double value = doubleValueConverter.convert(arguments[0], 0); if (count == 1) { reset(); return; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java index 246edc6bcd9..0d4c89d104b 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java @@ -23,6 +23,8 @@ import org.apache.iotdb.calc.execution.aggregation.CentralMomentAccumulator; import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask; import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.DoubleBigArray; import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray; +import org.apache.iotdb.calc.i18n.CalcMessages; +import org.apache.iotdb.calc.utils.TypeServices; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; @@ -30,6 +32,7 @@ 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.RunLengthEncodedColumn; +import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; @@ -45,7 +48,7 @@ public class GroupedCentralMomentAccumulator implements GroupedAccumulator { private static final int INTERMEDIATE_SIZE = Long.BYTES + 4 * Double.BYTES; private static final double EPSILON = 1e-12; - private final TSDataType seriesDataType; + private final TypeServices.ColumnToDoubleConverter doubleValueConverter; private final CentralMomentAccumulator.MomentType momentType; private final LongBigArray counts = new LongBigArray(); @@ -56,7 +59,15 @@ public class GroupedCentralMomentAccumulator implements GroupedAccumulator { public GroupedCentralMomentAccumulator( TSDataType seriesDataType, CentralMomentAccumulator.MomentType momentType) { - this.seriesDataType = seriesDataType; + this.doubleValueConverter = + TypeServices.NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE + .call(Type.fromTsDataType(seriesDataType)) + .create( + () -> + new UnSupportedDataTypeException( + String.format( + CalcMessages.UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION, + seriesDataType))); this.momentType = momentType; } @@ -85,7 +96,7 @@ public class GroupedCentralMomentAccumulator implements GroupedAccumulator { if (mask.isSelectAll()) { for (int i = 0; i < positionCount; i++) { if (!arguments[0].isNull(i)) { - update(groupIds[i], getDoubleValue(arguments[0], i)); + update(groupIds[i], doubleValueConverter.convert(arguments[0], i)); } } } else { @@ -93,31 +104,12 @@ public class GroupedCentralMomentAccumulator implements GroupedAccumulator { for (int i = 0; i < positionCount; i++) { int position = selectedPositions[i]; if (!arguments[0].isNull(position)) { - update(groupIds[position], getDoubleValue(arguments[0], position)); + update(groupIds[position], doubleValueConverter.convert(arguments[0], position)); } } } } - private double getDoubleValue(Column column, int position) { - switch (seriesDataType) { - case INT32: - case DATE: - return column.getInt(position); - case INT64: - case TIMESTAMP: - return column.getLong(position); - case FLOAT: - return column.getFloat(position); - case DOUBLE: - return column.getDouble(position); - default: - throw new UnSupportedDataTypeException( - String.format( - "Unsupported data type in CentralMoment Aggregation: %s", seriesDataType)); - } - } - private void update(int groupId, double value) { long n1 = counts.get(groupId); long n = n1 + 1; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/TypeServices.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/TypeServices.java index a70021361f4..a68dae23632 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/TypeServices.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/TypeServices.java @@ -30,9 +30,9 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.utils.datastructure.SortKey; +import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.file.metadata.enums.TSEncoding; -import org.apache.tsfile.read.common.type.service.IntTypeService; import org.apache.tsfile.read.common.type.service.TypeService; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.DateUtils; @@ -44,6 +44,7 @@ import java.util.Comparator; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.IntUnaryOperator; +import java.util.function.Supplier; import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtils.MIN_ARRAY_HEADER_SIZE; import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtils.MIN_OBJECT_HEADER_SIZE; @@ -82,12 +83,15 @@ public class TypeServices { Comparator.comparing( sortKey -> type.getBoolean(sortKey.tsBlock.getColumn(index), sortKey.rowIndex)); + // TypeService.check() must be able to build a strategy for every enum value. case ROW, UNKNOWN, VECTOR -> - throw new IllegalArgumentException( - String.format(CalcMessages.DATA_TYPE_CANNOT_BE_ORDERED, type)); + index -> { + throw new IllegalArgumentException( + String.format(CalcMessages.DATA_TYPE_CANNOT_BE_ORDERED, type)); + }; }; - public static final IntTypeService MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE = + public static final TypeService<Integer> MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE = type -> switch (type.getTypeEnum()) { case BOOLEAN -> 1; @@ -136,6 +140,19 @@ public class TypeServices { }; }; + // The caller supplies the exception so shared conversion keeps each aggregation API's contract. + public static final TypeService<ColumnToDoubleConverterFactory> + NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case INT32, DATE, INT64, TIMESTAMP, FLOAT, DOUBLE -> ignored -> type::getDouble; + case BOOLEAN, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR -> + exceptionSupplier -> + (column, position) -> { + throw exceptionSupplier.get(); + }; + }; + public static final TypeService<Function<DefaultEncodingProvider, TSEncoding>> DEFAULT_ENCODING_BY_TYPE_SERVICE = type -> @@ -272,6 +289,7 @@ public class TypeServices { MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE.check(); MEMORY_USAGE_OF_ONE_SERIALIZABLE_ROW_FIELD_SERVICE.check(); PRIMITIVE_TYPE_VALUE_EXTRACTOR_SERVICE.check(); + NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE.check(); DEFAULT_ENCODING_BY_TYPE_SERVICE.check(); DEFAULT_VALUE_WRITER_SERVICE.check(); INTERMEDIATE_VALUE_WRITER_SERVICE.check(); @@ -296,6 +314,16 @@ public class TypeServices { TSEncoding getDefaultTextEncoding(); } + @FunctionalInterface + public interface ColumnToDoubleConverter { + double convert(Column column, int position); + } + + @FunctionalInterface + public interface ColumnToDoubleConverterFactory { + ColumnToDoubleConverter create(Supplier<? extends RuntimeException> exceptionSupplier); + } + @FunctionalInterface public interface DefaultValueWriter { void write(Partition partition, int channel, int index, ColumnBuilder builder); diff --git a/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/utils/TypeServicesTest.java b/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/utils/TypeServicesTest.java new file mode 100644 index 00000000000..983d969387b --- /dev/null +++ b/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/utils/TypeServicesTest.java @@ -0,0 +1,98 @@ +/* + * 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.iotdb.calc.utils; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +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.type.Type; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class TypeServicesTest { + + @Test + public void testNumericColumnToDoubleConversion() { + assertEquals( + 1.0, convert(TSDataType.INT32, new IntColumn(1, Optional.empty(), new int[] {1})), 0.0); + assertEquals( + 2.0, + convert( + TSDataType.DATE, new IntColumn(1, Optional.empty(), new int[] {2}, TSDataType.DATE)), + 0.0); + assertEquals( + 3.0, convert(TSDataType.INT64, new LongColumn(1, Optional.empty(), new long[] {3L})), 0.0); + assertEquals( + 4.0, + convert(TSDataType.TIMESTAMP, new LongColumn(1, Optional.empty(), new long[] {4L})), + 0.0); + assertEquals( + 5.5, + convert(TSDataType.FLOAT, new FloatColumn(1, Optional.empty(), new float[] {5.5F})), + 0.0); + assertEquals( + 6.5, + convert(TSDataType.DOUBLE, new DoubleColumn(1, Optional.empty(), new double[] {6.5D})), + 0.0); + } + + @Test + public void testNumericColumnToDoubleConversionUsesCallerException() { + final UnsupportedOperationException expected = new UnsupportedOperationException("expected"); + final TypeServices.ColumnToDoubleConverter converter = + TypeServices.NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE + .call(Type.fromTsDataType(TSDataType.BOOLEAN)) + .create(() -> expected); + + final UnsupportedOperationException actual = + Assert.assertThrows( + UnsupportedOperationException.class, + () -> converter.convert(new IntColumn(1, Optional.empty(), new int[] {1}), 0)); + + assertSame(expected, actual); + } + + @Test + public void testUnsupportedMergeSortComparatorFailsOnUse() { + Assert.assertThrows( + IllegalArgumentException.class, + () -> + TypeServices.MERGE_SORT_COMPARATOR_SERVICE + .call(Type.fromTsDataType(TSDataType.VECTOR)) + .apply(0)); + } + + private static double convert(final TSDataType dataType, final Column column) { + return TypeServices.NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE + .call(Type.fromTsDataType(dataType)) + .create( + () -> + new IllegalStateException( + "supported data type should not use the exception factory")) + .convert(column, 0); + } +} diff --git a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java index 13369fc5ee3..a0087258f6b 100644 --- a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java +++ b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java @@ -1429,6 +1429,12 @@ public final class DataNodePipeMessages { public static final String FAILED_TO_UNBIND_FROM_PIPE_TSFILE_TO = "Failed to unbind from pipe tsfile to tablets metrics, pipe map is not empty, pipe: {}"; + // --------------------------------------------------------------------------- + // pipe – PipeRow + // --------------------------------------------------------------------------- + public static final String UNSUPPORTED_DATA_TYPE_FOR_COLUMN_FMT = + "unsupported data type %s for column %s"; + // --------------------------------------------------------------------------- // pipe – AbstractSameTypeNumericOperator // --------------------------------------------------------------------------- diff --git a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java index 49ee222c3e9..e768903bff2 100644 --- a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java +++ b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java @@ -1376,6 +1376,12 @@ public final class DataNodePipeMessages { public static final String FAILED_TO_UNBIND_FROM_PIPE_TSFILE_TO = "解绑 from pipe tsfile to tablets metrics, pipe map is not empty, pipe: {} 失败"; + // --------------------------------------------------------------------------- + // pipe – PipeRow + // --------------------------------------------------------------------------- + public static final String UNSUPPORTED_DATA_TYPE_FOR_COLUMN_FMT = + "不支持的数据类型 %s,列名为 %s"; + // --------------------------------------------------------------------------- // pipe – AbstractSameTypeNumericOperator // --------------------------------------------------------------------------- diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/row/PipeRow.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/row/PipeRow.java index b0897ed396a..e32d51adf95 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/row/PipeRow.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/row/PipeRow.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.pipe.event.common.row; +import org.apache.iotdb.db.utils.TypeServices; import org.apache.iotdb.pipe.api.access.Row; import org.apache.iotdb.pipe.api.exception.PipeParameterNotValidException; import org.apache.iotdb.pipe.api.type.Type; @@ -119,30 +120,9 @@ public class PipeRow implements Row { @Override public Object getObject(final int columnIndex) { - switch (getDataType(columnIndex)) { - case INT32: - return getInt(columnIndex); - case DATE: - return getDate(columnIndex); - case INT64: - case TIMESTAMP: - return getLong(columnIndex); - case FLOAT: - return getFloat(columnIndex); - case DOUBLE: - return getDouble(columnIndex); - case BOOLEAN: - return getBoolean(columnIndex); - case TEXT: - case BLOB: - case STRING: - return getBinary(columnIndex); - default: - throw new UnsupportedOperationException( - String.format( - "unsupported data type %s for column %s", - getDataType(columnIndex), columnNameStringList[columnIndex])); - } + return TypeServices.Pipe.PIPE_ROW_OBJECT_GETTER_SERVICE + .call(org.apache.tsfile.read.common.type.Type.fromTsDataType(valueColumnTypes[columnIndex])) + .get(this, columnIndex); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TypeServices.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TypeServices.java index c81d0d19ebc..295ebb30939 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TypeServices.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TypeServices.java @@ -86,6 +86,7 @@ import org.apache.iotdb.db.utils.datastructure.LongTVList; import org.apache.iotdb.db.utils.datastructure.TVList; import org.apache.iotdb.db.utils.windowing.window.EvictableBatchList; import org.apache.iotdb.db.utils.windowing.window.WindowImpl; +import org.apache.iotdb.pipe.api.access.Row; import org.apache.iotdb.rpc.RpcUtils; import com.google.common.io.BaseEncoding; @@ -3253,6 +3254,11 @@ public class TypeServices { Object get(Object column, int rowIndex); } + @FunctionalInterface + public interface PipeRowObjectGetter { + Object get(Row row, int columnIndex); + } + @FunctionalInterface public interface AggregateTabletColumnValueWriter { void write(Object column, int rowIndex, Object value); @@ -3312,6 +3318,26 @@ public class TypeServices { DataNodePipeMessages.INVALID_INPUT + type.getTypeEnum()); }; + public static final TypeService<PipeRowObjectGetter> PIPE_ROW_OBJECT_GETTER_SERVICE = + type -> + switch (type.getTypeEnum()) { + case BOOLEAN -> Row::getBoolean; + case INT32 -> Row::getInt; + case DATE -> Row::getDate; + case INT64, TIMESTAMP -> Row::getLong; + case FLOAT -> Row::getFloat; + case DOUBLE -> Row::getDouble; + case TEXT, BLOB, STRING -> Row::getBinary; + case OBJECT, ROW, UNKNOWN, VECTOR -> + (row, columnIndex) -> { + throw new UnsupportedOperationException( + String.format( + DataNodePipeMessages.UNSUPPORTED_DATA_TYPE_FOR_COLUMN_FMT, + row.getDataType(columnIndex), + row.getColumnName(columnIndex))); + }; + }; + public static final TypeService<TsPrimitiveTabletValueWriter> PIPE_TS_PRIMITIVE_TABLET_VALUE_WRITER_SERVICE = type -> @@ -3492,7 +3518,9 @@ public class TypeServices { OPC_UA_TABLET_OBJECT_VALUE_GETTER_SERVICE.check(); OPC_UA_DATA_TYPE_SERVICE.check(); PIPE_INSERT_EVENT_VALUE_LIST_TYPE_SERVICE.check(); - PIPE_DATA_TYPE_TRANSFORMER_SERVICE.check(); + // PIPE_DATA_TYPE_TRANSFORMER_SERVICE returns a value directly and intentionally rejects + // internal TsFile types, so the generic service check cannot be applied to it. + PIPE_ROW_OBJECT_GETTER_SERVICE.check(); PIPE_TS_PRIMITIVE_TABLET_VALUE_WRITER_SERVICE.check(); PIPE_BATCH_DATA_TABLET_VALUE_WRITER_SERVICE.check(); PIPE_TABLET_VALUE_COLUMN_FILTER_SERVICE.check(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/common/row/PipeRowTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/common/row/PipeRowTest.java new file mode 100644 index 00000000000..3699693f1ed --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/common/row/PipeRowTest.java @@ -0,0 +1,115 @@ +/* + * 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.iotdb.db.pipe.event.common.row; + +import org.apache.iotdb.pipe.api.type.Binary; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.utils.BytesUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDate; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class PipeRowTest { + + @Test + public void testGetObjectForSupportedTypes() { + final LocalDate date = LocalDate.of(2026, 8, 12); + final PipeRow row = + new PipeRow( + 0, + "root.test.device", + false, + null, + new long[] {1L}, + new TSDataType[] { + TSDataType.BOOLEAN, + TSDataType.INT32, + TSDataType.DATE, + TSDataType.INT64, + TSDataType.TIMESTAMP, + TSDataType.FLOAT, + TSDataType.DOUBLE, + TSDataType.TEXT, + TSDataType.BLOB, + TSDataType.STRING + }, + new Object[] { + new boolean[] {true}, + new int[] {2}, + new LocalDate[] {date}, + new long[] {3L}, + new long[] {4L}, + new float[] {5.5F}, + new double[] {6.5D}, + new org.apache.tsfile.utils.Binary[] {BytesUtils.valueOf("text")}, + new org.apache.tsfile.utils.Binary[] {BytesUtils.valueOf("blob")}, + new org.apache.tsfile.utils.Binary[] {BytesUtils.valueOf("string")} + }, + null, + new String[] { + "boolean", + "int32", + "date", + "int64", + "timestamp", + "float", + "double", + "text", + "blob", + "string" + }); + + assertEquals(Boolean.TRUE, row.getObject(0)); + assertEquals(2, row.getObject(1)); + assertEquals(date, row.getObject(2)); + assertEquals(3L, row.getObject(3)); + assertEquals(4L, row.getObject(4)); + assertEquals(5.5F, row.getObject(5)); + assertEquals(6.5D, row.getObject(6)); + assertBinaryEquals("text", row.getObject(7)); + assertBinaryEquals("blob", row.getObject(8)); + assertBinaryEquals("string", row.getObject(9)); + } + + @Test + public void testGetObjectForUnsupportedType() { + final PipeRow row = + new PipeRow( + 0, + "root.test.device", + false, + null, + new long[] {1L}, + new TSDataType[] {TSDataType.OBJECT}, + new Object[] {new Object[] {new Object()}}, + null, + new String[] {"object"}); + + Assert.assertThrows(IllegalArgumentException.class, () -> row.getObject(0)); + } + + private static void assertBinaryEquals(final String expected, final Object actual) { + assertArrayEquals(Binary.stringToBytes(expected), ((Binary) actual).getValues()); + } +}
