You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ji...@apache.org on 2026/09/08 01:14:41 UTC

(iotdb) branch remove_swtich_type updated (616b34b343d -> 64e1313f5b4)

This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a change to branch remove_swtich_type
in repository https://gitbox.apache.org/repos/asf/iotdb.git


    from 616b34b343d multiple refactors
     new 93a97ed6e95 multiple refactors
     new 85010f7ea88 multiple refactors
     new ec6e5de96b8 multiple refactors
     new e1f2247535d multiple refactors
     new 652cb02d014 multiple refactors
     new d54e316f9ee multiple refactors
     new 954731b1a51 multiple refactors
     new 814cb3c90e7 multiple refactors
     new 286f049c433 multiple refactors
     new ade03e2d1fd multiple refactors
     new 58a688e8f83 multiple refactors
     new 49a2e7a1d12 multiple refactors
     new f671bea1893 multiple refactors
     new 64e1313f5b4 multiple refactors

The 14 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 example/udf/pom.xml                                |    6 +
 .../java/org/apache/iotdb/udf/UDAFExample.java     |  106 +-
 .../v1/handler/StatementConstructionHandler.java   |  111 +-
 .../iotdb/rest/protocol/utils/TypeServices.java    |  162 +
 .../v1/handler/StatementConstructionHandler.java   |  111 +-
 .../v2/handler/StatementConstructionHandler.java   |  111 +-
 .../org/apache/iotdb/udf/api/utils/RowImpl.java    |   28 +-
 .../org/apache/iotdb/isession/SessionDataSet.java  |   38 +-
 .../org/apache/iotdb/isession/TypeServices.java    |   65 +
 .../org/apache/iotdb/rpc/IoTDBJDBCDataSet.java     |  177 +-
 .../java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java |   96 +-
 .../java/org/apache/iotdb/rpc/TypeServices.java    |  162 +
 .../iotdb/rpc/stmt/PreparedParameterSerde.java     |   41 +-
 .../org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java |  176 +
 .../iotdb/rpc/stmt/PreparedParameterSerdeTest.java |   12 +
 .../payload/SubscriptionRecordHandler.java         |   74 +-
 .../session/subscription/payload/TypeServices.java |  115 +
 .../subscription/payload/TypeServicesTest.java     |   69 +
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |   45 +
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |   45 +
 .../merge/comparator/JoinKeyComparatorFactory.java |   36 +-
 .../relational/MergeSortFullOuterJoinOperator.java |    6 +-
 .../AbstractApproxMostFrequentAccumulator.java     |    3 +-
 .../AbstractApproxPercentileAccumulator.java       |   59 +-
 .../relational/aggregation/AccumulatorFactory.java |   58 +-
 .../ApproxCountDistinctAccumulator.java            |   53 +-
 .../relational/aggregation/AvgAccumulator.java     |  180 +-
 .../relational/aggregation/ExtremeAccumulator.java |  167 +-
 .../relational/aggregation/FirstAccumulator.java   |  238 +-
 .../relational/aggregation/FirstByAccumulator.java |  219 +-
 .../relational/aggregation/LastAccumulator.java    |  268 +-
 .../relational/aggregation/LastByAccumulator.java  |  211 +-
 .../relational/aggregation/MaxAccumulator.java     |  189 +-
 .../relational/aggregation/MinAccumulator.java     |  189 +-
 .../aggregation/PercentileAccumulator.java         |  128 +-
 .../relational/aggregation/SumAccumulator.java     |  174 +-
 .../aggregation/TableMaxMinByBaseAccumulator.java  |  345 +-
 .../aggregation/TableModeAccumulator.java          |  668 +---
 .../source/relational/aggregation/Utils.java       |    2 -
 ...AbstractGroupedApproxPercentileAccumulator.java |   54 +-
 .../GroupedApproxCountDistinctAccumulator.java     |   52 +-
 .../aggregation/grouped/GroupedAvgAccumulator.java |  120 +-
 .../grouped/GroupedExtremeAccumulator.java         |  322 +-
 .../grouped/GroupedFirstAccumulator.java           |  443 +--
 .../grouped/GroupedFirstByAccumulator.java         |  446 +--
 .../grouped/GroupedLastAccumulator.java            |  442 +--
 .../grouped/GroupedLastByAccumulator.java          |  446 +--
 .../aggregation/grouped/GroupedMaxAccumulator.java |  351 +-
 .../grouped/GroupedMaxMinByBaseAccumulator.java    |  658 +---
 .../aggregation/grouped/GroupedMinAccumulator.java |  479 +--
 .../grouped/GroupedModeAccumulator.java            |  289 +-
 .../grouped/GroupedPercentileAccumulator.java      |   73 +-
 .../aggregation/grouped/GroupedSumAccumulator.java |  114 +-
 .../relational/ColumnTransformerBuilder.java       |  122 +-
 .../calc/plan/planner/CommonOperatorUtils.java     |  129 +-
 .../calc/plan/planner/TableOperatorGenerator.java  |  148 +-
 .../AbstractGreatestLeastColumnTransformer.java    |   46 +-
 .../multi/BinaryGreatestColumnTransformer.java     |    2 +-
 .../column/multi/BinaryLeastColumnTransformer.java |    2 +-
 .../multi/BooleanGreatestColumnTransformer.java    |    2 +-
 .../multi/BooleanLeastColumnTransformer.java       |    2 +-
 .../multi/DoubleGreatestColumnTransformer.java     |    2 +-
 .../column/multi/DoubleLeastColumnTransformer.java |    2 +-
 .../multi/FloatGreatestColumnTransformer.java      |    2 +-
 .../column/multi/FloatLeastColumnTransformer.java  |    2 +-
 .../multi/Int32GreatestColumnTransformer.java      |    2 +-
 .../column/multi/Int32LeastColumnTransformer.java  |    2 +-
 .../multi/Int64GreatestColumnTransformer.java      |    2 +-
 .../column/multi/Int64LeastColumnTransformer.java  |    2 +-
 .../dag/column/unary/InColumnTransformer.java      |  220 +-
 .../AbstractCastFunctionColumnTransformer.java     |  353 +-
 .../scalar/CastFunctionColumnTransformer.java      |   42 +-
 .../unary/scalar/FormatColumnTransformer.java      |   37 +-
 .../unary/scalar/RoundColumnTransformer.java       |   36 +-
 .../scalar/RoundFunctionColumnTransformer.java     |   37 +-
 .../scalar/TryCastFunctionColumnTransformer.java   |   43 +-
 .../transformation/dag/util/CastFunctionUtils.java |  246 +-
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 3471 +++++++++++++++++++-
 .../apache/iotdb/calc/utils/TypeServicesTest.java  |  206 ++
 .../processor/aggregate/AggregateProcessor.java    |   49 +-
 .../aggregate/TimeSeriesRuntimeState.java          |   21 +
 .../org/apache/iotdb/db/utils/TypeServices.java    |   51 +
 .../plan/relational/type/InternalTypeManager.java  |   67 +-
 .../iotdb/commons/udf/builtin/TypeServices.java    |  671 ++++
 .../apache/iotdb/commons/udf/builtin/UDTFAbs.java  |  106 +-
 .../iotdb/commons/udf/builtin/UDTFBottomK.java     |  105 +-
 .../commons/udf/builtin/UDTFChangePoints.java      |  178 +-
 .../commons/udf/builtin/UDTFCommonDerivative.java  |   41 +-
 .../udf/builtin/UDTFCommonValueDifference.java     |   42 +-
 .../iotdb/commons/udf/builtin/UDTFConst.java       |  267 +-
 .../udf/builtin/UDTFContinuouslySatisfy.java       |   79 +-
 .../iotdb/commons/udf/builtin/UDTFDerivative.java  |   11 +
 .../udf/builtin/UDTFEqualSizeBucketAggSample.java  |   53 +-
 .../udf/builtin/UDTFEqualSizeBucketM4Sample.java   |   39 +-
 .../builtin/UDTFEqualSizeBucketOutlierSample.java  |   53 +-
 .../builtin/UDTFEqualSizeBucketRandomSample.java   |   40 +-
 .../iotdb/commons/udf/builtin/UDTFInRange.java     |  162 +-
 .../apache/iotdb/commons/udf/builtin/UDTFM4.java   |   36 +-
 .../apache/iotdb/commons/udf/builtin/UDTFMath.java |  149 +-
 .../udf/builtin/UDTFNonNegativeDerivative.java     |   48 +-
 .../builtin/UDTFNonNegativeValueDifference.java    |   49 +-
 .../iotdb/commons/udf/builtin/UDTFOnOff.java       |  153 +-
 .../iotdb/commons/udf/builtin/UDTFSelectK.java     |  136 +-
 .../apache/iotdb/commons/udf/builtin/UDTFTopK.java |   65 +-
 .../commons/udf/builtin/UDTFValueDifference.java   |   12 +
 .../iotdb/commons/udf/builtin/UDTFValueTrend.java  |   42 +-
 .../commons/udf/utils/UDFDataTypeTransformer.java  |  107 +-
 .../relational/type/InternalTypeManagerTest.java   |   61 +
 .../udf/utils/UDFDataTypeTransformerTest.java      |   71 +
 library-udf/pom.xml                                |    5 +
 .../iotdb/library/i18n/LibraryUdfMessages.java     |   13 +
 .../iotdb/library/i18n/LibraryUdfMessages.java     |   12 +
 .../apache/iotdb/library/anomaly/UDTFKSigma.java   |   62 +-
 .../apache/iotdb/library/anomaly/UDTFRange.java    |   53 +-
 .../iotdb/library/anomaly/UDTFTwoSidedFilter.java  |   38 +-
 .../iotdb/library/dprofile/UDAFPercentile.java     |  218 +-
 .../iotdb/library/dprofile/UDAFQuantile.java       |  135 +-
 .../apache/iotdb/library/dprofile/UDAFSpread.java  |   99 +-
 .../iotdb/library/dprofile/UDTFDistinct.java       |  299 +-
 .../apache/iotdb/library/dprofile/UDTFSample.java  |   50 +-
 .../dprofile/util/ExactOrderStatistics.java        |  208 +-
 .../iotdb/library/drepair/UDTFTimestampRepair.java |   36 +-
 .../iotdb/library/drepair/UDTFValueFill.java       |   36 +-
 .../iotdb/library/drepair/UDTFValueRepair.java     |   37 +-
 .../library/frequency/UDFEnvelopeAnalysis.java     |   43 +-
 .../apache/iotdb/library/match/UDAFDTWMatch.java   |   26 +-
 .../iotdb/library/match/UDAFPatternMatch.java      |   26 +-
 .../apache/iotdb/library/util/TypeServices.java    |  300 ++
 .../java/org/apache/iotdb/library/util/Util.java   |   79 +-
 129 files changed, 8938 insertions(+), 10193 deletions(-)
 create mode 100644 external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/utils/TypeServices.java
 create mode 100644 iotdb-client/isession/src/main/java/org/apache/iotdb/isession/TypeServices.java
 create mode 100644 iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
 create mode 100644 iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
 create mode 100644 iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/TypeServices.java
 create mode 100644 iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/payload/TypeServicesTest.java
 create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
 create mode 100644 iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManagerTest.java
 create mode 100644 iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformerTest.java
 create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/util/TypeServices.java


(iotdb) 06/14: multiple refactors

Posted by ji...@apache.org.
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 d54e316f9ee4a5df47f5cc3305d7b3e3d6e86763
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 15:01:17 2026 +0800

    multiple refactors
---
 .../org/apache/iotdb/udf/api/utils/RowImpl.java    |  28 +-----
 .../commons/udf/utils/UDFDataTypeTransformer.java  | 107 ++++-----------------
 .../udf/utils/UDFDataTypeTransformerTest.java      |  71 ++++++++++++++
 3 files changed, 92 insertions(+), 114 deletions(-)

diff --git a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/utils/RowImpl.java b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/utils/RowImpl.java
index a082a1968ce..10b7dc7417d 100644
--- a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/utils/RowImpl.java
+++ b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/utils/RowImpl.java
@@ -129,30 +129,10 @@ public class RowImpl implements Row {
     if (tsDataType == null) {
       return null;
     }
-    byte type = tsDataType.getType();
-    switch (type) {
-      case 0:
-        return Type.BOOLEAN;
-      case 1:
-        return Type.INT32;
-      case 2:
-        return Type.INT64;
-      case 3:
-        return Type.FLOAT;
-      case 4:
-        return Type.DOUBLE;
-      case 5:
-        return Type.TEXT;
-      case 8:
-        return Type.TIMESTAMP;
-      case 9:
-        return Type.DATE;
-      case 10:
-        return Type.BLOB;
-      case 11:
-        return Type.STRING;
-      default:
-        throw new IllegalArgumentException(UdfApiMessages.INVALID_INPUT + type);
+    try {
+      return Type.valueOf(tsDataType.getType());
+    } catch (IllegalArgumentException e) {
+      throw new IllegalArgumentException(UdfApiMessages.INVALID_INPUT + tsDataType.getType(), e);
     }
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformer.java
index 163e9b4135a..e8f826b8e55 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformer.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformer.java
@@ -23,17 +23,6 @@ import org.apache.iotdb.commons.i18n.SchemaMessages;
 import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.read.common.type.BinaryType;
-import org.apache.tsfile.read.common.type.BlobType;
-import org.apache.tsfile.read.common.type.BooleanType;
-import org.apache.tsfile.read.common.type.DateType;
-import org.apache.tsfile.read.common.type.DoubleType;
-import org.apache.tsfile.read.common.type.FloatType;
-import org.apache.tsfile.read.common.type.IntType;
-import org.apache.tsfile.read.common.type.LongType;
-import org.apache.tsfile.read.common.type.ObjectType;
-import org.apache.tsfile.read.common.type.StringType;
-import org.apache.tsfile.read.common.type.TimestampType;
 
 import java.util.List;
 import java.util.stream.Collectors;
@@ -48,7 +37,14 @@ public class UDFDataTypeTransformer {
   }
 
   public static Type transformToUDFDataType(TSDataType tsDataType) {
-    return tsDataType == null ? null : getUDFDataType(tsDataType.getType());
+    if (tsDataType == null) {
+      return null;
+    }
+    try {
+      return Type.valueOf(tsDataType.getType());
+    } catch (IllegalArgumentException e) {
+      throw invalidInput(tsDataType, e);
+    }
   }
 
   public static List<Type> transformToUDFDataTypeList(List<TSDataType> tsDataTypeList) {
@@ -63,31 +59,10 @@ public class UDFDataTypeTransformer {
     if (type == null) {
       return null;
     }
-    switch (type.getTypeEnum()) {
-      case BOOLEAN:
-        return Type.BOOLEAN;
-      case INT32:
-        return Type.INT32;
-      case INT64:
-        return Type.INT64;
-      case FLOAT:
-        return Type.FLOAT;
-      case DOUBLE:
-        return Type.DOUBLE;
-      case TEXT:
-        return Type.TEXT;
-      case TIMESTAMP:
-        return Type.TIMESTAMP;
-      case DATE:
-        return Type.DATE;
-      case BLOB:
-        return Type.BLOB;
-      case STRING:
-        return Type.STRING;
-      case OBJECT:
-        return Type.OBJECT;
-      default:
-        throw new IllegalArgumentException(SchemaMessages.SCHEMA_INVALID_INPUT + type);
+    try {
+      return transformToUDFDataType(TSDataType.valueOf(type.getTypeEnum().name()));
+    } catch (IllegalArgumentException e) {
+      throw invalidInput(type, e);
     }
   }
 
@@ -95,60 +70,12 @@ public class UDFDataTypeTransformer {
     if (type == null) {
       return null;
     }
-    switch (type) {
-      case BOOLEAN:
-        return BooleanType.BOOLEAN;
-      case INT32:
-        return IntType.INT32;
-      case DATE:
-        return DateType.DATE;
-      case INT64:
-        return LongType.INT64;
-      case TIMESTAMP:
-        return TimestampType.TIMESTAMP;
-      case FLOAT:
-        return FloatType.FLOAT;
-      case DOUBLE:
-        return DoubleType.DOUBLE;
-      case TEXT:
-        return BinaryType.TEXT;
-      case BLOB:
-        return BlobType.BLOB;
-      case STRING:
-        return StringType.STRING;
-      case OBJECT:
-        return ObjectType.OBJECT;
-      default:
-        throw new IllegalArgumentException(SchemaMessages.SCHEMA_INVALID_INPUT + type);
-    }
+    return org.apache.tsfile.read.common.type.Type.fromTsDataType(
+        TSDataType.getTsDataType(type.getType()));
   }
 
-  private static Type getUDFDataType(byte type) {
-    switch (type) {
-      case 0:
-        return Type.BOOLEAN;
-      case 1:
-        return Type.INT32;
-      case 2:
-        return Type.INT64;
-      case 3:
-        return Type.FLOAT;
-      case 4:
-        return Type.DOUBLE;
-      case 5:
-        return Type.TEXT;
-      case 8:
-        return Type.TIMESTAMP;
-      case 9:
-        return Type.DATE;
-      case 10:
-        return Type.BLOB;
-      case 11:
-        return Type.STRING;
-      case 12:
-        return Type.OBJECT;
-      default:
-        throw new IllegalArgumentException(SchemaMessages.SCHEMA_INVALID_INPUT + type);
-    }
+  private static IllegalArgumentException invalidInput(
+      Object type, IllegalArgumentException cause) {
+    return new IllegalArgumentException(SchemaMessages.SCHEMA_INVALID_INPUT + type, cause);
   }
 }
diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformerTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformerTest.java
new file mode 100644
index 00000000000..8289b6906ce
--- /dev/null
+++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/utils/UDFDataTypeTransformerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.commons.udf.utils;
+
+import org.apache.iotdb.commons.i18n.SchemaMessages;
+import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.RowType;
+import org.apache.tsfile.read.common.type.UnknownType;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+
+public class UDFDataTypeTransformerTest {
+
+  @Test
+  public void testSupportedTypesUseTypeInterfaces() {
+    for (Type udfType : Type.allTypes()) {
+      TSDataType tsDataType = TSDataType.getTsDataType(udfType.getType());
+
+      Assert.assertEquals(udfType, UDFDataTypeTransformer.transformToUDFDataType(tsDataType));
+      org.apache.tsfile.read.common.type.Type readType =
+          UDFDataTypeTransformer.transformUDFDataTypeToReadType(udfType);
+      Assert.assertEquals(tsDataType.name(), readType.getTypeEnum().name());
+      Assert.assertEquals(udfType, UDFDataTypeTransformer.transformReadTypeToUDFDataType(readType));
+    }
+  }
+
+  @Test
+  public void testNullTypesRemainNull() {
+    Assert.assertNull(UDFDataTypeTransformer.transformToUDFDataType(null));
+    Assert.assertNull(UDFDataTypeTransformer.transformReadTypeToUDFDataType(null));
+    Assert.assertNull(UDFDataTypeTransformer.transformUDFDataTypeToReadType(null));
+  }
+
+  @Test
+  public void testInternalTypesRemainUnsupported() {
+    assertInvalidInput(() -> UDFDataTypeTransformer.transformToUDFDataType(TSDataType.UNKNOWN));
+    assertInvalidInput(() -> UDFDataTypeTransformer.transformToUDFDataType(TSDataType.VECTOR));
+    assertInvalidInput(
+        () -> UDFDataTypeTransformer.transformReadTypeToUDFDataType(UnknownType.UNKNOWN));
+    assertInvalidInput(
+        () ->
+            UDFDataTypeTransformer.transformReadTypeToUDFDataType(
+                RowType.anonymous(Collections.emptyList())));
+  }
+
+  private static void assertInvalidInput(Runnable conversion) {
+    IllegalArgumentException exception =
+        Assert.assertThrows(IllegalArgumentException.class, conversion::run);
+    Assert.assertTrue(exception.getMessage().startsWith(SchemaMessages.SCHEMA_INVALID_INPUT));
+  }
+}


(iotdb) 13/14: multiple refactors

Posted by ji...@apache.org.
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 f671bea18933747be49ca98195be67636e37706c
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Sep 2 12:25:17 2026 +0800

    multiple refactors
---
 example/udf/pom.xml                                |   6 +
 .../java/org/apache/iotdb/udf/UDAFExample.java     | 106 +--
 .../v1/handler/StatementConstructionHandler.java   | 111 +---
 .../iotdb/rest/protocol/utils/TypeServices.java    | 162 +++++
 .../v1/handler/StatementConstructionHandler.java   | 111 +---
 .../v2/handler/StatementConstructionHandler.java   | 111 +---
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |  42 ++
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |  42 ++
 .../AbstractApproxMostFrequentAccumulator.java     |   3 +-
 .../AbstractApproxPercentileAccumulator.java       |  35 +-
 .../relational/aggregation/AccumulatorFactory.java |   6 +-
 .../ApproxCountDistinctAccumulator.java            |  53 +-
 .../relational/aggregation/AvgAccumulator.java     | 180 +----
 .../relational/aggregation/ExtremeAccumulator.java | 127 +---
 .../relational/aggregation/FirstAccumulator.java   | 179 ++---
 .../relational/aggregation/FirstByAccumulator.java | 178 ++---
 .../relational/aggregation/LastAccumulator.java    | 210 +++---
 .../relational/aggregation/LastByAccumulator.java  | 177 ++---
 .../relational/aggregation/MaxAccumulator.java     | 143 +---
 .../relational/aggregation/MinAccumulator.java     | 143 +---
 .../aggregation/PercentileAccumulator.java         | 128 +---
 .../relational/aggregation/SumAccumulator.java     | 174 +----
 .../aggregation/TableMaxMinByBaseAccumulator.java  | 345 +---------
 .../aggregation/TableModeAccumulator.java          | 668 ++-----------------
 .../source/relational/aggregation/Utils.java       |   2 -
 ...AbstractGroupedApproxPercentileAccumulator.java |  31 +-
 .../GroupedApproxCountDistinctAccumulator.java     |  52 +-
 .../aggregation/grouped/GroupedAvgAccumulator.java | 120 +---
 .../grouped/GroupedExtremeAccumulator.java         |  25 +-
 .../grouped/GroupedFirstAccumulator.java           |  18 +-
 .../grouped/GroupedFirstByAccumulator.java         |  19 +-
 .../grouped/GroupedLastAccumulator.java            |  18 +-
 .../grouped/GroupedLastByAccumulator.java          |  19 +-
 .../aggregation/grouped/GroupedMaxAccumulator.java |  17 +-
 .../grouped/GroupedMaxMinByBaseAccumulator.java    |  34 +-
 .../aggregation/grouped/GroupedMinAccumulator.java |  17 +-
 .../grouped/GroupedModeAccumulator.java            | 289 ++------
 .../grouped/GroupedPercentileAccumulator.java      |  73 +-
 .../aggregation/grouped/GroupedSumAccumulator.java | 114 +---
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 740 ++++++++++++++++++++-
 .../apache/iotdb/calc/utils/TypeServicesTest.java  |  45 ++
 .../processor/aggregate/AggregateProcessor.java    |  49 +-
 .../aggregate/TimeSeriesRuntimeState.java          |  21 +
 .../org/apache/iotdb/db/utils/TypeServices.java    |  51 ++
 library-udf/pom.xml                                |   5 +
 .../iotdb/library/i18n/LibraryUdfMessages.java     |  13 +
 .../iotdb/library/i18n/LibraryUdfMessages.java     |  12 +
 .../apache/iotdb/library/anomaly/UDTFKSigma.java   |  62 +-
 .../apache/iotdb/library/anomaly/UDTFRange.java    |  53 +-
 .../iotdb/library/anomaly/UDTFTwoSidedFilter.java  |  38 +-
 .../iotdb/library/dprofile/UDAFPercentile.java     | 218 +++---
 .../iotdb/library/dprofile/UDAFQuantile.java       | 135 ++--
 .../apache/iotdb/library/dprofile/UDAFSpread.java  |  99 +--
 .../iotdb/library/dprofile/UDTFDistinct.java       | 299 +++++----
 .../apache/iotdb/library/dprofile/UDTFSample.java  |  50 +-
 .../dprofile/util/ExactOrderStatistics.java        | 208 +++---
 .../iotdb/library/drepair/UDTFTimestampRepair.java |  36 +-
 .../iotdb/library/drepair/UDTFValueFill.java       |  36 +-
 .../iotdb/library/drepair/UDTFValueRepair.java     |  37 +-
 .../library/frequency/UDFEnvelopeAnalysis.java     |  43 +-
 .../apache/iotdb/library/match/UDAFDTWMatch.java   |  26 +-
 .../iotdb/library/match/UDAFPatternMatch.java      |  26 +-
 .../apache/iotdb/library/util/TypeServices.java    | 300 +++++++++
 .../java/org/apache/iotdb/library/util/Util.java   |  79 +--
 64 files changed, 2931 insertions(+), 4038 deletions(-)

diff --git a/example/udf/pom.xml b/example/udf/pom.xml
index 5036e4ab3b2..2268fd8c096 100644
--- a/example/udf/pom.xml
+++ b/example/udf/pom.xml
@@ -39,6 +39,12 @@
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.tsfile</groupId>
+            <artifactId>tsfile</artifactId>
+            <version>${tsfile.version}</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
     <profiles>
         <profile>
diff --git a/example/udf/src/main/java/org/apache/iotdb/udf/UDAFExample.java b/example/udf/src/main/java/org/apache/iotdb/udf/UDAFExample.java
index 540f6bcef5c..bdf880bed10 100644
--- a/example/udf/src/main/java/org/apache/iotdb/udf/UDAFExample.java
+++ b/example/udf/src/main/java/org/apache/iotdb/udf/UDAFExample.java
@@ -29,6 +29,8 @@ import org.apache.iotdb.udf.api.type.Type;
 import org.apache.iotdb.udf.api.utils.ResultValue;
 
 import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.service.TypeService;
 import org.apache.tsfile.utils.BitMap;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
@@ -71,7 +73,7 @@ public class UDAFExample implements UDAF {
     }
   }
 
-  private Type dataType;
+  private ColumnNumericReader valueReader;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -82,7 +84,10 @@ public class UDAFExample implements UDAF {
 
   @Override
   public void beforeStart(UDFParameters parameters, UDAFConfigurations configurations) {
-    dataType = parameters.getDataType(0);
+    valueReader =
+        COLUMN_NUMERIC_READER_SERVICE.call(
+            org.apache.tsfile.read.common.type.Type.fromTsDataType(
+                TSDataType.getTsDataType(parameters.getDataType(0).getType())));
     configurations.setOutputDataType(Type.DOUBLE);
   }
 
@@ -95,28 +100,15 @@ public class UDAFExample implements UDAF {
   public void addInput(State state, Column[] columns, BitMap bitMap) {
     AvgState avgState = (AvgState) state;
 
-    switch (dataType) {
-      case INT32:
-        addIntInput(avgState, columns, bitMap);
-        return;
-      case INT64:
-        addLongInput(avgState, columns, bitMap);
-        return;
-      case FLOAT:
-        addFloatInput(avgState, columns, bitMap);
-        return;
-      case DOUBLE:
-        addDoubleInput(avgState, columns, bitMap);
-        return;
-      case TEXT:
-      case BOOLEAN:
-      case TIMESTAMP:
-      case STRING:
-      case BLOB:
-      case DATE:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in aggregation AVG : %s", dataType));
+    int count = columns[0].getPositionCount();
+    for (int i = 0; i < count; i++) {
+      if (bitMap != null && !bitMap.isMarked(i)) {
+        continue;
+      }
+      if (!columns[0].isNull(i)) {
+        avgState.count++;
+        avgState.sum += valueReader.read(columns[0], i);
+      }
     }
   }
 
@@ -149,55 +141,21 @@ public class UDAFExample implements UDAF {
     avgState.sum -= avgRhs.sum;
   }
 
-  private void addIntInput(AvgState state, Column[] columns, BitMap bitMap) {
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (bitMap != null && !bitMap.isMarked(i)) {
-        continue;
-      }
-      if (!columns[0].isNull(i)) {
-        state.count++;
-        state.sum += columns[0].getInt(i);
-      }
-    }
-  }
-
-  private void addLongInput(AvgState avgState, Column[] columns, BitMap bitMap) {
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (bitMap != null && !bitMap.isMarked(i)) {
-        continue;
-      }
-      if (!columns[0].isNull(i)) {
-        avgState.count++;
-        avgState.sum += columns[0].getLong(i);
-      }
-    }
-  }
-
-  private void addFloatInput(AvgState avgState, Column[] columns, BitMap bitMap) {
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (bitMap != null && !bitMap.isMarked(i)) {
-        continue;
-      }
-      if (!columns[0].isNull(i)) {
-        avgState.count++;
-        avgState.sum += columns[0].getFloat(i);
-      }
-    }
-  }
-
-  private void addDoubleInput(AvgState avgState, Column[] columns, BitMap bitMap) {
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (bitMap != null && !bitMap.isMarked(i)) {
-        continue;
-      }
-      if (!columns[0].isNull(i)) {
-        avgState.count++;
-        avgState.sum += columns[0].getDouble(i);
-      }
-    }
+  private static final TypeService<ColumnNumericReader> COLUMN_NUMERIC_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> Column::getInt;
+            case INT64 -> Column::getLong;
+            case FLOAT -> Column::getFloat;
+            case DOUBLE -> Column::getDouble;
+            default ->
+                (column, index) -> {
+                  throw new UnSupportedDataTypeException("Unsupported data type: " + type);
+                };
+          };
+
+  @FunctionalInterface
+  private interface ColumnNumericReader {
+    double read(Column column, int index);
   }
 }
diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/StatementConstructionHandler.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/StatementConstructionHandler.java
index 28cf4d0c1b1..b433b48de94 100644
--- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/StatementConstructionHandler.java
+++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/StatementConstructionHandler.java
@@ -23,15 +23,14 @@ import org.apache.iotdb.commons.queryengine.utils.TimestampPrecisionUtils;
 import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
 import org.apache.iotdb.db.exception.WriteProcessRejectException;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
-import org.apache.iotdb.rest.i18n.RestMessages;
 import org.apache.iotdb.rest.protocol.table.v1.model.InsertTabletRequest;
+import org.apache.iotdb.rest.protocol.utils.TypeServices;
 
 import org.apache.tsfile.enums.ColumnCategory;
 import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.BitMap;
 
-import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Locale;
 
@@ -65,107 +64,11 @@ public class StatementConstructionHandler {
 
     for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
       bitMaps[columnIndex] = new BitMap(rowSize);
-      switch (dataTypes[columnIndex]) {
-        case BOOLEAN:
-          boolean[] booleanValues = new boolean[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(rowIndex).get(columnIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              if ("1".equals(data.toString())) {
-                booleanValues[rowIndex] = true;
-              } else if ("0".equals(data.toString())) {
-                booleanValues[rowIndex] = false;
-              } else {
-                booleanValues[rowIndex] = (Boolean) data;
-              }
-            }
-          }
-          columns[columnIndex] = booleanValues;
-          break;
-        case INT32:
-        case DATE:
-          int[] intValues = new int[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(rowIndex).get(columnIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              intValues[rowIndex] = (int) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = intValues;
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long[] longValues = new long[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(rowIndex).get(columnIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              longValues[rowIndex] = (int) object;
-            } else if (object instanceof Long) {
-              longValues[rowIndex] = (long) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = longValues;
-          break;
-        case FLOAT:
-          float[] floatValues = new float[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(rowIndex).get(columnIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              floatValues[rowIndex] = Float.parseFloat(String.valueOf(data));
-            }
-          }
-          columns[columnIndex] = floatValues;
-          break;
-        case DOUBLE:
-          double[] doubleValues = new double[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(rowIndex).get(columnIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              doubleValues[rowIndex] =
-                  Double.parseDouble(String.valueOf(rawData.get(rowIndex).get(columnIndex)));
-            }
-          }
-          columns[columnIndex] = doubleValues;
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-          Binary[] binaryValues = new Binary[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(rowIndex).get(columnIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-              binaryValues[rowIndex] = new Binary("".getBytes(StandardCharsets.UTF_8));
-            } else {
-              binaryValues[rowIndex] =
-                  new Binary(
-                      rawData
-                          .get(rowIndex)
-                          .get(columnIndex)
-                          .toString()
-                          .getBytes(StandardCharsets.UTF_8));
-            }
-          }
-          columns[columnIndex] = binaryValues;
-          break;
-        default:
-          throw new IllegalArgumentException(
-              RestMessages.INVALID_INPUT + rawDataType.get(columnIndex));
-      }
+      final int column = columnIndex;
+      columns[columnIndex] =
+          TypeServices.INSERT_TABLET_COLUMN_WRITER_SERVICE
+              .call(Type.fromTsDataType(dataTypes[columnIndex]))
+              .write(rowIndex -> rawData.get(rowIndex).get(column), rowSize, bitMaps[columnIndex]);
     }
     insertStatement.setColumns(columns);
     insertStatement.setBitMaps(bitMaps);
diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/utils/TypeServices.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/utils/TypeServices.java
new file mode 100644
index 00000000000..e1974882471
--- /dev/null
+++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/utils/TypeServices.java
@@ -0,0 +1,162 @@
+/*
+ * 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.rest.protocol.utils;
+
+import org.apache.iotdb.db.exception.WriteProcessRejectException;
+import org.apache.iotdb.rest.i18n.RestMessages;
+
+import org.apache.tsfile.read.common.type.service.TypeService;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.BitMap;
+
+import java.nio.charset.StandardCharsets;
+
+/** Type-specific column writers used by the REST insert handlers. */
+public final class TypeServices {
+
+  public static final TypeService<ColumnWriter> INSERT_TABLET_COLUMN_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> TypeServices::writeBooleanColumn;
+            case INT32, DATE -> TypeServices::writeIntColumn;
+            case INT64, TIMESTAMP -> TypeServices::writeLongColumn;
+            case FLOAT -> TypeServices::writeFloatColumn;
+            case DOUBLE -> TypeServices::writeDoubleColumn;
+            case TEXT, BLOB, STRING -> TypeServices::writeBinaryColumn;
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (valueAccessor, rowCount, bitMap) -> {
+                  throw new IllegalArgumentException(
+                      RestMessages.INVALID_INPUT + type.getTypeEnum());
+                };
+          };
+
+  static {
+    INSERT_TABLET_COLUMN_WRITER_SERVICE.check();
+  }
+
+  private static Object writeBooleanColumn(
+      ValueAccessor valueAccessor, int rowCount, BitMap bitMap) {
+    boolean[] values = new boolean[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+      } else if ("1".equals(data.toString())) {
+        values[rowIndex] = true;
+      } else if ("0".equals(data.toString())) {
+        values[rowIndex] = false;
+      } else {
+        values[rowIndex] = (Boolean) data;
+      }
+    }
+    return values;
+  }
+
+  private static Object writeIntColumn(ValueAccessor valueAccessor, int rowCount, BitMap bitMap)
+      throws WriteProcessRejectException {
+    int[] values = new int[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+      } else if (data instanceof Integer) {
+        values[rowIndex] = (int) data;
+      } else {
+        throw unsupportedValue(data);
+      }
+    }
+    return values;
+  }
+
+  private static Object writeLongColumn(ValueAccessor valueAccessor, int rowCount, BitMap bitMap)
+      throws WriteProcessRejectException {
+    long[] values = new long[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+      } else if (data instanceof Integer) {
+        values[rowIndex] = (int) data;
+      } else if (data instanceof Long) {
+        values[rowIndex] = (long) data;
+      } else {
+        throw unsupportedValue(data);
+      }
+    }
+    return values;
+  }
+
+  private static Object writeFloatColumn(ValueAccessor valueAccessor, int rowCount, BitMap bitMap) {
+    float[] values = new float[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+      } else {
+        values[rowIndex] = Float.parseFloat(String.valueOf(data));
+      }
+    }
+    return values;
+  }
+
+  private static Object writeDoubleColumn(
+      ValueAccessor valueAccessor, int rowCount, BitMap bitMap) {
+    double[] values = new double[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+      } else {
+        values[rowIndex] = Double.parseDouble(String.valueOf(data));
+      }
+    }
+    return values;
+  }
+
+  private static Object writeBinaryColumn(
+      ValueAccessor valueAccessor, int rowCount, BitMap bitMap) {
+    Binary[] values = new Binary[rowCount];
+    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+      Object data = valueAccessor.get(rowIndex);
+      if (data == null) {
+        bitMap.mark(rowIndex);
+        values[rowIndex] = new Binary(new byte[0]);
+      } else {
+        values[rowIndex] = new Binary(data.toString().getBytes(StandardCharsets.UTF_8));
+      }
+    }
+    return values;
+  }
+
+  private static WriteProcessRejectException unsupportedValue(Object value) {
+    return new WriteProcessRejectException("unsupported data type: " + value.getClass());
+  }
+
+  @FunctionalInterface
+  public interface ValueAccessor {
+    Object get(int rowIndex);
+  }
+
+  @FunctionalInterface
+  public interface ColumnWriter {
+    Object write(ValueAccessor valueAccessor, int rowCount, BitMap bitMap)
+        throws WriteProcessRejectException;
+  }
+
+  private TypeServices() {}
+}
diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/handler/StatementConstructionHandler.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/handler/StatementConstructionHandler.java
index 9fbd9ac6696..6343d1a8f5a 100644
--- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/handler/StatementConstructionHandler.java
+++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/handler/StatementConstructionHandler.java
@@ -23,14 +23,13 @@ import org.apache.iotdb.commons.utils.PathUtils;
 import org.apache.iotdb.db.exception.WriteProcessRejectException;
 import org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.DataNodeDevicePathCache;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
-import org.apache.iotdb.rest.i18n.RestMessages;
+import org.apache.iotdb.rest.protocol.utils.TypeServices;
 import org.apache.iotdb.rest.protocol.v1.model.InsertTabletRequest;
 
 import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.BitMap;
 
-import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Locale;
 
@@ -66,107 +65,11 @@ public class StatementConstructionHandler {
 
     for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
       bitMaps[columnIndex] = new BitMap(rowSize);
-      switch (dataTypes[columnIndex]) {
-        case BOOLEAN:
-          boolean[] booleanValues = new boolean[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(columnIndex).get(rowIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              if ("1".equals(data.toString())) {
-                booleanValues[rowIndex] = true;
-              } else if ("0".equals(data.toString())) {
-                booleanValues[rowIndex] = false;
-              } else {
-                booleanValues[rowIndex] = (Boolean) data;
-              }
-            }
-          }
-          columns[columnIndex] = booleanValues;
-          break;
-        case INT32:
-        case DATE:
-          int[] intValues = new int[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(columnIndex).get(rowIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              intValues[rowIndex] = (int) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = intValues;
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long[] longValues = new long[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(columnIndex).get(rowIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              longValues[rowIndex] = (int) object;
-            } else if (object instanceof Long) {
-              longValues[rowIndex] = (long) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = longValues;
-          break;
-        case FLOAT:
-          float[] floatValues = new float[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(columnIndex).get(rowIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              floatValues[rowIndex] = Float.parseFloat(String.valueOf(data));
-            }
-          }
-          columns[columnIndex] = floatValues;
-          break;
-        case DOUBLE:
-          double[] doubleValues = new double[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(columnIndex).get(rowIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              doubleValues[rowIndex] =
-                  Double.parseDouble(String.valueOf(rawData.get(columnIndex).get(rowIndex)));
-            }
-          }
-          columns[columnIndex] = doubleValues;
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-          Binary[] binaryValues = new Binary[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(columnIndex).get(rowIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-              binaryValues[rowIndex] = new Binary("".getBytes(StandardCharsets.UTF_8));
-            } else {
-              binaryValues[rowIndex] =
-                  new Binary(
-                      rawData
-                          .get(columnIndex)
-                          .get(rowIndex)
-                          .toString()
-                          .getBytes(StandardCharsets.UTF_8));
-            }
-          }
-          columns[columnIndex] = binaryValues;
-          break;
-        default:
-          throw new IllegalArgumentException(
-              RestMessages.INVALID_INPUT + rawDataType.get(columnIndex));
-      }
+      final int column = columnIndex;
+      columns[columnIndex] =
+          TypeServices.INSERT_TABLET_COLUMN_WRITER_SERVICE
+              .call(Type.fromTsDataType(dataTypes[columnIndex]))
+              .write(rowIndex -> rawData.get(column).get(rowIndex), rowSize, bitMaps[columnIndex]);
     }
 
     insertStatement.setTimes(
diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/handler/StatementConstructionHandler.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/handler/StatementConstructionHandler.java
index 679c4556e90..770648a8cdb 100644
--- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/handler/StatementConstructionHandler.java
+++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/handler/StatementConstructionHandler.java
@@ -26,17 +26,16 @@ import org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.DataNodeDeviceP
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
-import org.apache.iotdb.rest.i18n.RestMessages;
 import org.apache.iotdb.rest.protocol.utils.InsertRowDataUtils;
+import org.apache.iotdb.rest.protocol.utils.TypeServices;
 import org.apache.iotdb.rest.protocol.v2.model.InsertRecordsRequest;
 import org.apache.iotdb.rest.protocol.v2.model.InsertTabletRequest;
 import org.apache.iotdb.rpc.IoTDBConnectionException;
 
 import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.BitMap;
 
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -76,107 +75,11 @@ public class StatementConstructionHandler {
 
     for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
       bitMaps[columnIndex] = new BitMap(rowSize);
-      switch (dataTypes[columnIndex]) {
-        case BOOLEAN:
-          boolean[] booleanValues = new boolean[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(columnIndex).get(rowIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              if ("1".equals(data.toString())) {
-                booleanValues[rowIndex] = true;
-              } else if ("0".equals(data.toString())) {
-                booleanValues[rowIndex] = false;
-              } else {
-                booleanValues[rowIndex] = (Boolean) data;
-              }
-            }
-          }
-          columns[columnIndex] = booleanValues;
-          break;
-        case INT32:
-        case DATE:
-          int[] intValues = new int[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(columnIndex).get(rowIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              intValues[rowIndex] = (int) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = intValues;
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long[] longValues = new long[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object object = rawData.get(columnIndex).get(rowIndex);
-            if (object == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else if (object instanceof Integer) {
-              longValues[rowIndex] = (int) object;
-            } else if (object instanceof Long) {
-              longValues[rowIndex] = (long) object;
-            } else {
-              throw new WriteProcessRejectException(
-                  "unsupported data type: " + object.getClass().toString());
-            }
-          }
-          columns[columnIndex] = longValues;
-          break;
-        case FLOAT:
-          float[] floatValues = new float[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            Object data = rawData.get(columnIndex).get(rowIndex);
-            if (data == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              floatValues[rowIndex] = Float.parseFloat(String.valueOf(data));
-            }
-          }
-          columns[columnIndex] = floatValues;
-          break;
-        case DOUBLE:
-          double[] doubleValues = new double[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(columnIndex).get(rowIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-            } else {
-              doubleValues[rowIndex] =
-                  Double.parseDouble(String.valueOf(rawData.get(columnIndex).get(rowIndex)));
-            }
-          }
-          columns[columnIndex] = doubleValues;
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-          Binary[] binaryValues = new Binary[rowSize];
-          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
-            if (rawData.get(columnIndex).get(rowIndex) == null) {
-              bitMaps[columnIndex].mark(rowIndex);
-              binaryValues[rowIndex] = new Binary("".getBytes(StandardCharsets.UTF_8));
-            } else {
-              binaryValues[rowIndex] =
-                  new Binary(
-                      rawData
-                          .get(columnIndex)
-                          .get(rowIndex)
-                          .toString()
-                          .getBytes(StandardCharsets.UTF_8));
-            }
-          }
-          columns[columnIndex] = binaryValues;
-          break;
-        default:
-          throw new IllegalArgumentException(
-              RestMessages.INVALID_INPUT + rawDataType.get(columnIndex));
-      }
+      final int column = columnIndex;
+      columns[columnIndex] =
+          TypeServices.INSERT_TABLET_COLUMN_WRITER_SERVICE
+              .call(Type.fromTsDataType(dataTypes[columnIndex]))
+              .write(rowIndex -> rawData.get(column).get(rowIndex), rowSize, bitMaps[columnIndex]);
     }
 
     insertStatement.setTimes(
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 688797980fc..36667b2596a 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
@@ -35,6 +35,20 @@ public final class CalcMessages {
       "CountAllAccumulator does not support statistics.";
   public static final String COUNT_IF_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
       "CountIfAccumulator does not support statistics";
+  public static final String PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "PercentileAccumulator does not support statistics";
+  public static final String PERCENTILE_REQUIRES_TWO_ARGUMENTS =
+      "PERCENTILE requires 2 arguments, but got %d";
+  public static final String APPROX_PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxPercentileAccumulator does not support statistics";
+  public static final String APPROX_COUNT_DISTINCT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxCountDistinctAccumulator does not support statistics";
+  public static final String APPROX_MOST_FREQUENT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxMostFrequentAccumulator does not support statistics";
+  public static final String NO_EXACT_DOUBLE_REPRESENTATION_FOR_LONG =
+      "no exact double representation for long: %s";
+  public static final String APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS =
+      "APPROX_PERCENTILE requires 2 or 3 arguments, but got %d";
   public static final String CURRENT_COLUMN_IS_NOT_OBJECT_COLUMN =
       "current column is not object column";
   public static final String CURRENT_TS_BLOCK_SIZE_IS = "Current tsBlock size is : {}";
@@ -133,6 +147,34 @@ 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_AGGREGATION_AVG =
+      "Unsupported data type in aggregation AVG : %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_SUM_AGGREGATION =
+      "Unsupported data type in SUM Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_PERCENTILE_AGGREGATION =
+      "Unsupported data type in Percentile Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION =
+      "Unsupported data type in APPROX_PERCENTILE Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION =
+      "Unsupported data type in APPROX_COUNT_DISTINCT Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION =
+      "Unsupported data type in FIRST Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION =
+      "Unsupported data type in LAST Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION =
+      "Unsupported data type in MAX Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION =
+      "Unsupported data type in MIN Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION =
+      "Unsupported data type in FIRST_BY Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION =
+      "Unsupported data type in LAST_BY Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION =
+      "Unsupported data type in EXTREME Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION =
+      "Unsupported data type in MAX_BY/MIN_BY Aggregation: %s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MODE_AGGREGATION =
+      "Unsupported data type : %s";
   public static final String UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION =
       "Unsupported data type in CentralMoment Aggregation: %s";
   public static final String UNSUPPORTED_DATA_TYPE_IN_AGGREGATION_VARIANCE =
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 84088a79202..133971f7460 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
@@ -34,6 +34,20 @@ public final class CalcMessages {
       "CountAllAccumulator 不支持统计信息。";
   public static final String COUNT_IF_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
       "CountIfAccumulator 不支持统计信息";
+  public static final String PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "PercentileAccumulator 不支持统计信息";
+  public static final String PERCENTILE_REQUIRES_TWO_ARGUMENTS =
+      "PERCENTILE 需要 2 个参数,但收到 %d 个参数";
+  public static final String APPROX_PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxPercentileAccumulator 不支持统计信息";
+  public static final String APPROX_COUNT_DISTINCT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxCountDistinctAccumulator 不支持统计信息";
+  public static final String APPROX_MOST_FREQUENT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
+      "ApproxMostFrequentAccumulator 不支持统计信息";
+  public static final String NO_EXACT_DOUBLE_REPRESENTATION_FOR_LONG =
+      "long 无法精确表示为 double:%s";
+  public static final String APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS =
+      "APPROX_PERCENTILE 需要 2 或 3 个参数,但收到 %d 个参数";
   public static final String CURRENT_COLUMN_IS_NOT_OBJECT_COLUMN = "当前列不是对象列";
   public static final String CURRENT_TS_BLOCK_SIZE_IS = "当前 tsBlock 大小为:{}";
   public static final String DATA_TYPE_CANNOT_BE_ORDERED = "数据类型:%s 不能排序";
@@ -126,6 +140,34 @@ 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_AGGREGATION_AVG =
+      "AVG 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_SUM_AGGREGATION =
+      "SUM 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_PERCENTILE_AGGREGATION =
+      "Percentile 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION =
+      "APPROX_PERCENTILE 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION =
+      "APPROX_COUNT_DISTINCT 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION =
+      "FIRST 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION =
+      "LAST 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION =
+      "MAX 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION =
+      "MIN 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION =
+      "FIRST_BY 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION =
+      "LAST_BY 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION =
+      "EXTREME 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION =
+      "MAX_BY/MIN_BY 聚合中不支持的数据类型:%s";
+  public static final String UNSUPPORTED_DATA_TYPE_IN_MODE_AGGREGATION =
+      "不支持的数据类型:%s";
   public static final String UNSUPPORTED_DATA_TYPE_IN_CENTRAL_MOMENT_AGGREGATION =
       "CentralMoment 聚合中不支持的数据类型:%s";
   public static final String UNSUPPORTED_DATA_TYPE_IN_AGGREGATION_VARIANCE =
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxMostFrequentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxMostFrequentAccumulator.java
index d595e5ef711..0b2d9ab5f0a 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxMostFrequentAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxMostFrequentAccumulator.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.SpaceSavingStateFactory;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import com.google.gson.Gson;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -52,7 +53,7 @@ public abstract class AbstractApproxMostFrequentAccumulator<T> implements TableA
   @Override
   public void addStatistics(Statistics[] statistics) {
     throw new UnsupportedOperationException(
-        "ApproxMostFrequentAccumulator does not support statistics");
+        CalcMessages.APPROX_MOST_FREQUENT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
index 861c27e856f..a23895de741 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
@@ -20,12 +20,15 @@
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.TDigest;
+import org.apache.iotdb.calc.i18n.CalcMessages;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.exception.SemanticException;
 
 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.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -39,10 +42,13 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
 
   protected final TDigest tDigest = new TDigest();
   protected final TSDataType seriesDataType;
+  private final TypeServices.NumericResultWriter resultWriter;
   protected double percentage;
 
   AbstractApproxPercentileAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
+    this.resultWriter =
+        TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(Type.fromTsDataType(seriesDataType));
   }
 
   @Override
@@ -64,7 +70,7 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
     } else {
       throw new SemanticException(
           String.format(
-              "APPROX_PERCENTILE requires 2 or 3 arguments, but got %d", arguments.length));
+              CalcMessages.APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS, arguments.length));
     }
     switch (seriesDataType) {
       case INT32:
@@ -83,7 +89,8 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
       default:
         throw new UnSupportedDataTypeException(
             String.format(
-                "Unsupported data type in APPROX_PERCENTILE Aggregation: %s", seriesDataType));
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
+                seriesDataType));
     }
   }
 
@@ -118,25 +125,7 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt((int) result);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong((long) result);
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat((float) result);
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(result);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                "Unsupported data type in APPROX_PERCENTILE Aggregation: %s", seriesDataType));
-    }
+    resultWriter.write(columnBuilder, result);
   }
 
   @Override
@@ -147,7 +136,7 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
   @Override
   public void addStatistics(Statistics[] statistics) {
     throw new UnsupportedOperationException(
-        "ApproxPercentileAccumulator does not support statistics");
+        CalcMessages.APPROX_PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS);
   }
 
   @Override
@@ -167,7 +156,7 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
     double doubleValue = (double) value;
     if ((long) doubleValue != value) {
       throw new SemanticException(
-          String.format("no exact double representation for long: %s", value));
+          String.format(CalcMessages.NO_EXACT_DOUBLE_REPRESENTATION_FOR_LONG, value));
     }
     return value;
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
index f9d35cf67f5..342ae536a8e 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
@@ -527,7 +527,8 @@ public class AccumulatorFactory {
       case OBJECT:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in APPROX_COUNT_DISTINCT Aggregation: %s", type));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION, type));
     }
   }
 
@@ -553,7 +554,8 @@ public class AccumulatorFactory {
       case OBJECT:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in APPROX_COUNT_DISTINCT Aggregation: %s", type));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION, type));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java
index 1106abb57b9..77e8a7c400d 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java
@@ -21,15 +21,17 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLogStateFactory;
+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.block.column.BinaryColumnBuilder;
+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;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog.DEFAULT_STANDARD_ERROR;
@@ -38,6 +40,7 @@ public class ApproxCountDistinctAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(ApproxCountDistinctAccumulator.class);
   private final TSDataType seriesDataType;
+  private final TypeServices.HyperLogLogColumnAdder valueAdder;
   private final HyperLogLogStateFactory.SingleHyperLogLogState state =
       HyperLogLogStateFactory.createSingleState();
 
@@ -45,6 +48,8 @@ public class ApproxCountDistinctAccumulator implements TableAccumulator {
 
   public ApproxCountDistinctAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
+    this.valueAdder =
+        TypeServices.HYPER_LOG_LOG_COLUMN_ADDER_SERVICE.call(Type.fromTsDataType(seriesDataType));
   }
 
   @Override
@@ -65,34 +70,22 @@ public class ApproxCountDistinctAccumulator implements TableAccumulator {
         arguments.length == 1 ? DEFAULT_STANDARD_ERROR : arguments[1].getDouble(0);
     HyperLogLog hll = getOrCreateHyperLogLog(state, maxStandardError);
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], mask, hll);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], mask, hll);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask, hll);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask, hll);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments[0], mask, hll);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], mask, hll);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                "Unsupported data type in APPROX_COUNT_DISTINCT Aggregation: %s", seriesDataType));
+    Column valueColumn = arguments[0];
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        if (!valueColumn.isNull(i)) {
+          valueAdder.add(hll, valueColumn, i);
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!valueColumn.isNull(position)) {
+          valueAdder.add(hll, valueColumn, position);
+        }
+      }
     }
   }
 
@@ -128,7 +121,7 @@ public class ApproxCountDistinctAccumulator implements TableAccumulator {
   @Override
   public void addStatistics(Statistics[] statistics) {
     throw new UnsupportedOperationException(
-        "ApproxCountDistinctAccumulator does not support statistics");
+        CalcMessages.APPROX_COUNT_DISTINCT_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java
index 9d142151a10..438597499b4 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java
@@ -18,6 +18,9 @@
  */
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -26,6 +29,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -37,12 +41,23 @@ public class AvgAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(AvgAccumulator.class);
   private final TSDataType argumentDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
   private long countValue;
   private double sumValue;
   private boolean initResult = false;
 
   public AvgAccumulator(TSDataType argumentDataType) {
     this.argumentDataType = argumentDataType;
+    Type type = Type.fromTsDataType(argumentDataType);
+    this.valueConverter =
+        TypeServices.AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_AGGREGATION_AVG,
+                            argumentDataType)));
   }
 
   @Override
@@ -58,59 +73,13 @@ public class AvgAccumulator implements TableAccumulator {
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of AVG should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        addIntInput(arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in aggregation AVG : %s", argumentDataType));
-    }
+    addInput(arguments[0], mask);
   }
 
   @Override
   public void removeInput(Column[] arguments) {
     checkArgument(arguments.length == 1, "argument of AVG should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        removeIntInput(arguments[0]);
-        return;
-      case INT64:
-        removeLongInput(arguments[0]);
-        return;
-      case FLOAT:
-        removeFloatInput(arguments[0]);
-        return;
-      case DOUBLE:
-        removeDoubleInput(arguments[0]);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in aggregation AVG : %s", argumentDataType));
-    }
+    removeInput(arguments[0]);
   }
 
   @Override
@@ -196,57 +165,7 @@ public class AvgAccumulator implements TableAccumulator {
     return bytes;
   }
 
-  private void addIntInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getInt(i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getInt(position);
-        }
-      }
-    }
-  }
-
-  private void addLongInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getLong(i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getLong(position);
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(Column column, AggregationMask mask) {
+  private void addInput(Column column, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
     if (mask.isSelectAll()) {
@@ -254,7 +173,7 @@ public class AvgAccumulator implements TableAccumulator {
         if (!column.isNull(i)) {
           initResult = true;
           countValue++;
-          sumValue += column.getFloat(i);
+          sumValue += valueConverter.convert(column, i);
         }
       }
     } else {
@@ -265,73 +184,18 @@ public class AvgAccumulator implements TableAccumulator {
         if (!column.isNull(position)) {
           initResult = true;
           countValue++;
-          sumValue += column.getFloat(position);
+          sumValue += valueConverter.convert(column, position);
         }
       }
     }
   }
 
-  private void addDoubleInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getDouble(i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          countValue++;
-          sumValue += column.getDouble(position);
-        }
-      }
-    }
-  }
-
-  private void removeIntInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        countValue--;
-        sumValue -= column.getInt(i);
-      }
-    }
-  }
-
-  private void removeLongInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        countValue--;
-        sumValue -= column.getLong(i);
-      }
-    }
-  }
-
-  private void removeFloatInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        countValue--;
-        sumValue -= column.getFloat(i);
-      }
-    }
-  }
-
-  private void removeDoubleInput(Column column) {
+  private void removeInput(Column column) {
     int count = column.getPositionCount();
     for (int i = 0; i < count; i++) {
       if (!column.isNull(i)) {
         countValue--;
-        sumValue -= column.getDouble(i);
+        sumValue -= valueConverter.convert(column, i);
       }
     }
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
index 806250fb3b1..8b97c1849e1 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -31,14 +34,17 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 public class ExtremeAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(ExtremeAccumulator.class);
-  private static final String UNSUPPORTED_DATA_TYPE = "Unsupported data type in EXTREME: %s";
   private final TSDataType seriesDataType;
+  private final Type type;
   private final TsPrimitiveType extremeResult;
+  private final TypeServices.ColumnValueUpdater valueUpdater;
   private boolean initResult;
 
   public ExtremeAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    this.extremeResult = Type.fromTsDataType(seriesDataType).getTsPrimitiveType();
+    this.type = Type.fromTsDataType(seriesDataType);
+    this.extremeResult = type.getTsPrimitiveType();
+    this.valueUpdater = TypeServices.EXTREME_COLUMN_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -53,29 +59,22 @@ public class ExtremeAccumulator implements TableAccumulator {
 
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(UNSUPPORTED_DATA_TYPE, seriesDataType));
+    Column column = arguments[0];
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        if (!column.isNull(i)) {
+          initResult |= valueUpdater.update(extremeResult, column, i, initResult);
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!column.isNull(position)) {
+          initResult |= valueUpdater.update(extremeResult, column, position, initResult);
+        }
+      }
     }
   }
 
@@ -86,30 +85,7 @@ public class ExtremeAccumulator implements TableAccumulator {
         continue;
       }
 
-      switch (seriesDataType) {
-        case INT32:
-          updateIntResult(argument.getInt(i));
-          break;
-        case INT64:
-          updateLongResult(argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatResult(argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleResult(argument.getDouble(i));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-        case BOOLEAN:
-        case DATE:
-        case TIMESTAMP:
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(UNSUPPORTED_DATA_TYPE, seriesDataType));
-      }
+      initResult |= valueUpdater.update(extremeResult, argument, i, initResult);
     }
   }
 
@@ -145,7 +121,8 @@ public class ExtremeAccumulator implements TableAccumulator {
       case TIMESTAMP:
       default:
         throw new UnSupportedDataTypeException(
-            String.format(UNSUPPORTED_DATA_TYPE, seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
   }
 
@@ -156,30 +133,7 @@ public class ExtremeAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt(extremeResult.getInt());
-        break;
-      case INT64:
-        columnBuilder.writeLong(extremeResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(extremeResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(extremeResult.getDouble());
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(UNSUPPORTED_DATA_TYPE, seriesDataType));
-    }
+    type.write(columnBuilder, extremeResult);
   }
 
   @Override
@@ -189,30 +143,7 @@ public class ExtremeAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt(extremeResult.getInt());
-        break;
-      case INT64:
-        columnBuilder.writeLong(extremeResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(extremeResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(extremeResult.getDouble());
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(UNSUPPORTED_DATA_TYPE, seriesDataType));
-    }
+    type.write(columnBuilder, extremeResult);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
index cc8e6e546fe..183c83526db 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -29,11 +32,11 @@ 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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -44,6 +47,8 @@ public class FirstAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(FirstAccumulator.class);
   protected final TSDataType seriesDataType;
+  protected final Type type;
+  private final TypeServices.PrimitiveColumnValueSetter valueSetter;
   protected TsPrimitiveType firstValue;
   protected long minTime = Long.MAX_VALUE;
   protected boolean initResult = false;
@@ -52,7 +57,9 @@ public class FirstAccumulator implements TableAccumulator {
 
   public FirstAccumulator(TSDataType seriesDataType, boolean canFinishAfterInit) {
     this.seriesDataType = seriesDataType;
-    this.firstValue = Type.fromTsDataType(seriesDataType).getTsPrimitiveType();
+    this.type = Type.fromTsDataType(seriesDataType);
+    this.firstValue = type.getTsPrimitiveType();
+    this.valueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(type);
     this.canFinishAfterInit = canFinishAfterInit;
   }
 
@@ -69,34 +76,7 @@ public class FirstAccumulator implements TableAccumulator {
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
     // arguments[0] is value column, arguments[1] is time column
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], arguments[1], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], arguments[1], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], arguments[1], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], arguments[1], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments[0], arguments[1], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], arguments[1], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST: %s", seriesDataType));
-    }
+    addInput(arguments[0], arguments[1], mask);
   }
 
   @Override
@@ -112,70 +92,14 @@ public class FirstAccumulator implements TableAccumulator {
         continue;
       }
 
-      byte[] bytes = argument.getBinary(i).getValues();
-      long time = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 0);
-      boolean isTimeNull = BytesUtils.bytesToBool(bytes, 8);
-      int offset = 9;
-
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          int intVal = BytesUtils.bytesToInt(bytes, offset);
-          if (!isTimeNull) {
-            updateIntFirstValue(intVal, time);
-          } else {
-            updateIntNullTimeValue(intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isTimeNull) {
-            updateLongFirstValue(longVal, time);
-          } else {
-            updateLongNullTimeValue(longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = BytesUtils.bytesToFloat(bytes, offset);
-          if (!isTimeNull) {
-            updateFloatFirstValue(floatVal, time);
-          } else {
-            updateFloatNullTimeValue(floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = BytesUtils.bytesToDouble(bytes, offset);
-          if (!isTimeNull) {
-            updateDoubleFirstValue(doubleVal, time);
-          } else {
-            updateDoubleNullTimeValue(doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          Binary binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          if (!isTimeNull) {
-            updateBinaryFirstValue(binaryVal, time);
-          } else {
-            updateBinaryNullTimeValue(binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = BytesUtils.bytesToBool(bytes, offset);
-          if (!isTimeNull) {
-            updateBooleanFirstValue(boolVal, time);
-          } else {
-            updateBooleanNullTimeValue(boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+      ByteBuffer buffer = ByteBuffer.wrap(argument.getBinary(i).getValues());
+      long time = buffer.getLong();
+      boolean isTimeNull = buffer.get() != 0;
+      TsPrimitiveType value = type.deserialize(buffer);
+      if (!isTimeNull) {
+        updateFirstValue(value, time);
+      } else {
+        updateNullTimeValue(value);
       }
     }
   }
@@ -208,34 +132,7 @@ public class FirstAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(firstValue.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(firstValue.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(firstValue.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(firstValue.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-      case OBJECT:
-        columnBuilder.writeBinary(firstValue.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(firstValue.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, firstValue);
   }
 
   @Override
@@ -243,6 +140,30 @@ public class FirstAccumulator implements TableAccumulator {
     return canFinishAfterInit && initResult;
   }
 
+  private void addInput(Column valueColumn, Column timeColumn, AggregationMask mask) {
+    int selectPositionCount = mask.getSelectedPositionCount();
+    boolean isSelectAll = mask.isSelectAll();
+    int[] selectedPositions = isSelectAll ? null : mask.getSelectedPositions();
+
+    for (int i = 0; i < selectPositionCount; i++) {
+      int position = isSelectAll ? i : selectedPositions[i];
+      if (valueColumn.isNull(position)) {
+        continue;
+      }
+
+      if (!timeColumn.isNull(position)) {
+        if (checkAndUpdateFirstTime(timeColumn.getLong(position))) {
+          valueSetter.set(firstValue, valueColumn, position);
+        }
+        if (canFinishAfterInit) {
+          return;
+        }
+      } else if (checkAndUpdateNullTime()) {
+        valueSetter.set(firstValue, valueColumn, position);
+      }
+    }
+  }
+
   @Override
   public void addStatistics(Statistics[] statistics) {
     if (statistics == null || statistics[0] == null) {
@@ -294,7 +215,7 @@ public class FirstAccumulator implements TableAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -323,6 +244,18 @@ public class FirstAccumulator implements TableAccumulator {
     return false;
   }
 
+  private void updateFirstValue(TsPrimitiveType value, long curTime) {
+    if (checkAndUpdateFirstTime(curTime)) {
+      firstValue.copy(value);
+    }
+  }
+
+  private void updateNullTimeValue(TsPrimitiveType value) {
+    if (checkAndUpdateNullTime()) {
+      firstValue.copy(value);
+    }
+  }
+
   /**
    * Updates the accumulator, prioritizing values with valid timestamps over those with null
    * timestamps.
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
index dc57942769f..496485cb684 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -33,6 +36,7 @@ import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -51,6 +55,8 @@ public class FirstByAccumulator implements TableAccumulator {
 
   private long yFirstTime = Long.MAX_VALUE;
 
+  private final Type xType;
+  private final TypeServices.PrimitiveColumnValueSetter xValueSetter;
   private final TsPrimitiveType xResult;
   private boolean xIsNull = true;
 
@@ -70,7 +76,9 @@ public class FirstByAccumulator implements TableAccumulator {
     this.xIsTimeColumn = xIsTimeColumn;
     this.yIsTimeColumn = yIsTimeColumn;
 
-    this.xResult = Type.fromTsDataType(xDataType).getTsPrimitiveType();
+    this.xType = Type.fromTsDataType(xDataType);
+    this.xValueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(xType);
+    this.xResult = xType.getTsPrimitiveType();
     this.canFinishAfterInit = canFinishAfterInit;
   }
 
@@ -90,34 +98,7 @@ public class FirstByAccumulator implements TableAccumulator {
     checkArgument(arguments.length == 3, "Length of input Column[] for FIRST_BY should be 3");
 
     // arguments[0] is x column, arguments[1] is y column, arguments[2] is time column
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", yDataType));
-    }
+    addInput(arguments[0], arguments[1], arguments[2], mask);
   }
 
   @Override
@@ -141,78 +122,16 @@ public class FirstByAccumulator implements TableAccumulator {
       boolean isXValueNull = BytesUtils.bytesToBool(bytes, offset);
       offset += 1;
 
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          int intVal = isXValueNull ? 0 : BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntFirstValue(isXValueNull, intVal, curTime);
-          } else {
-            updateIntNullTimeValue(isXValueNull, intVal);
-          }
-          break;
-
-        case INT64:
-        case TIMESTAMP:
-          long longVal =
-              isXValueNull ? 0 : BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongFirstValue(isXValueNull, longVal, curTime);
-          } else {
-            updateLongNullTimeValue(isXValueNull, longVal);
-          }
-          break;
-
-        case FLOAT:
-          float floatVal = isXValueNull ? 0 : BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatFirstValue(isXValueNull, floatVal, curTime);
-          } else {
-            updateFloatNullTimeValue(isXValueNull, floatVal);
-          }
-          break;
-
-        case DOUBLE:
-          double doubleVal = isXValueNull ? 0 : BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleFirstValue(isXValueNull, doubleVal, curTime);
-          } else {
-            updateDoubleNullTimeValue(isXValueNull, doubleVal);
-          }
-          break;
-
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          Binary binaryVal = null;
-          if (!isXValueNull) {
-            int length = BytesUtils.bytesToInt(bytes, offset);
-            offset += Integer.BYTES;
-            binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          }
-          if (!isOrderTimeNull) {
-            updateBinaryFirstValue(isXValueNull, binaryVal, curTime);
-          } else {
-            updateBinaryNullTimeValue(isXValueNull, binaryVal);
-          }
-          break;
-
-        case BOOLEAN:
-          boolean boolVal = false;
-          if (!isXValueNull) {
-            boolVal = BytesUtils.bytesToBool(bytes, offset);
-          }
-          if (!isOrderTimeNull) {
-            updateBooleanFirstValue(isXValueNull, boolVal, curTime);
-          } else {
-            updateBooleanNullTimeValue(isXValueNull, boolVal);
-          }
-          break;
-
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in FIRST_BY Aggregation: %s", yDataType));
+      TsPrimitiveType value =
+          isXValueNull
+              ? null
+              : xType.deserialize(ByteBuffer.wrap(bytes, offset, bytes.length - offset));
+      if (!isOrderTimeNull) {
+        if (checkAndUpdateFirstTime(isXValueNull, curTime) && !isXValueNull) {
+          xResult.copy(value);
+        }
+      } else if (checkAndUpdateNullTime(isXValueNull) && !isXValueNull) {
+        xResult.copy(value);
       }
     }
   }
@@ -243,34 +162,7 @@ public class FirstByAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xResult.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(xResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
-    }
+    xType.write(columnBuilder, xResult);
   }
 
   @Override
@@ -335,7 +227,8 @@ public class FirstByAccumulator implements TableAccumulator {
               break;
             default:
               throw new UnSupportedDataTypeException(
-                  String.format("Unsupported data type in FIRST_BY Aggregation: %s", yDataType));
+                  String.format(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, yDataType));
           }
         }
       }
@@ -390,6 +283,31 @@ public class FirstByAccumulator implements TableAccumulator {
     return false;
   }
 
+  private void addInput(Column xColumn, Column yColumn, Column timeColumn, AggregationMask mask) {
+    int selectPositionCount = mask.getSelectedPositionCount();
+    boolean isSelectAll = mask.isSelectAll();
+    int[] selectedPositions = isSelectAll ? null : mask.getSelectedPositions();
+
+    for (int i = 0; i < selectPositionCount; i++) {
+      int position = isSelectAll ? i : selectedPositions[i];
+      if (yColumn.isNull(position)) {
+        continue;
+      }
+
+      boolean isXValueNull = xColumn.isNull(position);
+      if (!timeColumn.isNull(position)) {
+        if (checkAndUpdateFirstTime(isXValueNull, timeColumn.getLong(position)) && !isXValueNull) {
+          xValueSetter.set(xResult, xColumn, position);
+        }
+        if (canFinishAfterInit) {
+          return;
+        }
+      } else if (checkAndUpdateNullTime(isXValueNull) && !isXValueNull) {
+        xValueSetter.set(xResult, xColumn, position);
+      }
+    }
+  }
+
   protected void addIntInput(
       Column xColumn, Column yColumn, Column timeColumn, AggregationMask mask) {
     int selectPositionCount = mask.getSelectedPositionCount();
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
index 7a71cb16bcb..8bad5c9bc0a 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -39,11 +42,14 @@ import java.nio.charset.StandardCharsets;
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull;
 
-public class LastAccumulator implements TableAccumulator {
+public class LastAccumulator implements TableAccumulator, TypeServices.LastValueUpdater {
 
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(LastAccumulator.class);
   protected final TSDataType seriesDataType;
+  protected final Type type;
+  private final TypeServices.PrimitiveColumnValueSetter valueSetter;
+  private final TypeServices.LastValueDeserializer valueDeserializer;
   protected TsPrimitiveType lastValue;
   protected long maxTime = Long.MIN_VALUE;
   protected boolean initResult = false;
@@ -51,7 +57,10 @@ public class LastAccumulator implements TableAccumulator {
 
   public LastAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    this.lastValue = Type.fromTsDataType(seriesDataType).getTsPrimitiveType();
+    this.type = Type.fromTsDataType(seriesDataType);
+    this.lastValue = type.getTsPrimitiveType();
+    this.valueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(type);
+    this.valueDeserializer = TypeServices.LAST_VALUE_DESERIALIZER_SERVICE.call(type);
   }
 
   public boolean hasInitResult() {
@@ -79,34 +88,7 @@ public class LastAccumulator implements TableAccumulator {
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
     // arguments[0] is value column, arguments[1] is time column
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], arguments[1], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], arguments[1], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], arguments[1], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], arguments[1], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments[0], arguments[1], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], arguments[1], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST: %s", seriesDataType));
-    }
+    addInput(arguments[0], arguments[1], mask);
   }
 
   @Override
@@ -126,67 +108,7 @@ public class LastAccumulator implements TableAccumulator {
       long time = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 0);
       boolean isOrderTimeNull = BytesUtils.bytesToBool(bytes, Long.BYTES);
       int offset = Long.BYTES + 1;
-
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          int intVal = BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntLastValue(intVal, time);
-          } else {
-            updateIntNullTimeValue(intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongLastValue(longVal, time);
-          } else {
-            updateLongNullTimeValue(longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatLastValue(floatVal, time);
-          } else {
-            updateFloatNullTimeValue(floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleLastValue(doubleVal, time);
-          } else {
-            updateDoubleNullTimeValue(doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          Binary binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          if (!isOrderTimeNull) {
-            updateBinaryLastValue(binaryVal, time);
-          } else {
-            updateBinaryNullTimeValue(binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = BytesUtils.bytesToBool(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateBooleanLastValue(boolVal, time);
-          } else {
-            updateBooleanNullTimeValue(boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
-      }
+      valueDeserializer.deserialize(bytes, offset, time, isOrderTimeNull, this);
     }
   }
 
@@ -216,34 +138,7 @@ public class LastAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(lastValue.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(lastValue.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(lastValue.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(lastValue.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(lastValue.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(lastValue.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, lastValue);
   }
 
   @Override
@@ -251,6 +146,81 @@ public class LastAccumulator implements TableAccumulator {
     return false;
   }
 
+  private void addInput(Column valueColumn, Column timeColumn, AggregationMask mask) {
+    int selectPositionCount = mask.getSelectedPositionCount();
+    boolean isSelectAll = mask.isSelectAll();
+    int[] selectedPositions = isSelectAll ? null : mask.getSelectedPositions();
+
+    for (int i = 0; i < selectPositionCount; i++) {
+      int position = isSelectAll ? i : selectedPositions[i];
+      if (valueColumn.isNull(position)) {
+        continue;
+      }
+
+      if (!timeColumn.isNull(position)) {
+        if (checkAndUpdateLastTime(timeColumn.getLong(position))) {
+          valueSetter.set(lastValue, valueColumn, position);
+        }
+      } else if (checkAndUpdateNullTime()) {
+        valueSetter.set(lastValue, valueColumn, position);
+      }
+    }
+  }
+
+  @Override
+  public void updateInt(int value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateIntNullTimeValue(value);
+    } else {
+      updateIntLastValue(value, time);
+    }
+  }
+
+  @Override
+  public void updateLong(long value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateLongNullTimeValue(value);
+    } else {
+      updateLongLastValue(value, time);
+    }
+  }
+
+  @Override
+  public void updateFloat(float value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateFloatNullTimeValue(value);
+    } else {
+      updateFloatLastValue(value, time);
+    }
+  }
+
+  @Override
+  public void updateDouble(double value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateDoubleNullTimeValue(value);
+    } else {
+      updateDoubleLastValue(value, time);
+    }
+  }
+
+  @Override
+  public void updateBinary(Binary value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateBinaryNullTimeValue(value);
+    } else {
+      updateBinaryLastValue(value, time);
+    }
+  }
+
+  @Override
+  public void updateBoolean(boolean value, long time, boolean isOrderTimeNull) {
+    if (isOrderTimeNull) {
+      updateBooleanNullTimeValue(value);
+    } else {
+      updateBooleanLastValue(value, time);
+    }
+  }
+
   @Override
   public void addStatistics(Statistics[] statistics) {
     if (statistics == null || statistics[0] == null) {
@@ -302,7 +272,7 @@ public class LastAccumulator implements TableAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
index 6999a06df80..e466bbea9be 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -33,6 +36,8 @@ import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
+import java.nio.ByteBuffer;
+
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull;
 
@@ -49,6 +54,8 @@ public class LastByAccumulator implements TableAccumulator {
 
   private long yLastTime = Long.MIN_VALUE;
 
+  private final Type xType;
+  private final TypeServices.PrimitiveColumnValueSetter xValueSetter;
   private final TsPrimitiveType xResult;
   private boolean xIsNull = true;
 
@@ -61,7 +68,9 @@ public class LastByAccumulator implements TableAccumulator {
     this.yDataType = yDataType;
     this.xIsTimeColumn = xIsTimeColumn;
     this.yIsTimeColumn = yIsTimeColumn;
-    this.xResult = Type.fromTsDataType(xDataType).getTsPrimitiveType();
+    this.xType = Type.fromTsDataType(xDataType);
+    this.xValueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(xType);
+    this.xResult = xType.getTsPrimitiveType();
   }
 
   public boolean hasInitResult() {
@@ -95,34 +104,7 @@ public class LastByAccumulator implements TableAccumulator {
     checkArgument(arguments.length == 3, "Length of input Column[] for LAST_BY should be 3");
 
     // arguments[0] is x column, arguments[1] is y column, arguments[2] is time column
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], arguments[1], arguments[2], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", yDataType));
-    }
+    addInput(arguments[0], arguments[1], arguments[2], mask);
   }
 
   @Override
@@ -146,79 +128,16 @@ public class LastByAccumulator implements TableAccumulator {
       boolean isXValueNull = BytesUtils.bytesToBool(bytes, offset);
       offset += 1;
 
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          // if the x value is null, could not serialize the x value
-          int xIntVal = isXValueNull ? 0 : BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntLastValue(isXValueNull, xIntVal, curTime);
-          } else {
-            updateIntNullTimeValue(isXValueNull, xIntVal);
-          }
-          break;
-
-        case INT64:
-        case TIMESTAMP:
-          long longVal =
-              isXValueNull ? 0 : BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongLastValue(isXValueNull, longVal, curTime);
-          } else {
-            updateLongNullTimeValue(isXValueNull, longVal);
-          }
-          break;
-
-        case FLOAT:
-          float floatVal = isXValueNull ? 0 : BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatLastValue(isXValueNull, floatVal, curTime);
-          } else {
-            updateFloatNullTimeValue(isXValueNull, floatVal);
-          }
-          break;
-
-        case DOUBLE:
-          double doubleVal = isXValueNull ? 0 : BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleLastValue(isXValueNull, doubleVal, curTime);
-          } else {
-            updateDoubleNullTimeValue(isXValueNull, doubleVal);
-          }
-          break;
-
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          Binary binaryVal = null;
-          if (!isXValueNull) {
-            int length = BytesUtils.bytesToInt(bytes, offset);
-            offset += Integer.BYTES;
-            binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          }
-          if (!isOrderTimeNull) {
-            updateBinaryLastValue(isXValueNull, binaryVal, curTime);
-          } else {
-            updateBinaryNullTimeValue(isXValueNull, binaryVal);
-          }
-          break;
-
-        case BOOLEAN:
-          boolean boolVal = false;
-          if (!isXValueNull) {
-            boolVal = BytesUtils.bytesToBool(bytes, offset);
-          }
-          if (!isOrderTimeNull) {
-            updateBooleanLastValue(isXValueNull, boolVal, curTime);
-          } else {
-            updateBooleanNullTimeValue(isXValueNull, boolVal);
-          }
-          break;
-
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in LAST_BY Aggregation: %s", yDataType));
+      TsPrimitiveType value =
+          isXValueNull
+              ? null
+              : xType.deserialize(ByteBuffer.wrap(bytes, offset, bytes.length - offset));
+      if (!isOrderTimeNull) {
+        if (checkAndUpdateLastTime(isXValueNull, curTime) && !isXValueNull) {
+          xResult.copy(value);
+        }
+      } else if (checkAndUpdateNullTime(isXValueNull) && !isXValueNull) {
+        xResult.copy(value);
       }
     }
   }
@@ -249,34 +168,7 @@ public class LastByAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xResult.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-      case OBJECT:
-        columnBuilder.writeBinary(xResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
-    }
+    xType.write(columnBuilder, xResult);
   }
 
   @Override
@@ -335,7 +227,8 @@ public class LastByAccumulator implements TableAccumulator {
               break;
             default:
               throw new UnSupportedDataTypeException(
-                  String.format("Unsupported data type in LAST_BY Aggregation: %s", yDataType));
+                  String.format(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, yDataType));
           }
         }
       }
@@ -359,6 +252,28 @@ public class LastByAccumulator implements TableAccumulator {
     this.xResult.reset();
   }
 
+  private void addInput(Column xColumn, Column yColumn, Column timeColumn, AggregationMask mask) {
+    int selectPositionCount = mask.getSelectedPositionCount();
+    boolean isSelectAll = mask.isSelectAll();
+    int[] selectedPositions = isSelectAll ? null : mask.getSelectedPositions();
+
+    for (int i = 0; i < selectPositionCount; i++) {
+      int position = isSelectAll ? i : selectedPositions[i];
+      if (yColumn.isNull(position)) {
+        continue;
+      }
+
+      boolean isXValueNull = xColumn.isNull(position);
+      if (!timeColumn.isNull(position)) {
+        if (checkAndUpdateLastTime(isXValueNull, timeColumn.getLong(position)) && !isXValueNull) {
+          xValueSetter.set(xResult, xColumn, position);
+        }
+      } else if (checkAndUpdateNullTime(isXValueNull) && !isXValueNull) {
+        xValueSetter.set(xResult, xColumn, position);
+      }
+    }
+  }
+
   private boolean checkAndUpdateLastTime(boolean isXValueNull, long curTime) {
     if (!initResult || curTime > yLastTime) {
       initResult = true;
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
index a81ca1b62ae..887916bcb48 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -38,12 +41,16 @@ public class MaxAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(MaxAccumulator.class);
   private final TSDataType seriesDataType;
+  private final Type type;
+  private final TypeServices.ColumnValueUpdater valueUpdater;
   private final TsPrimitiveType maxResult;
   private boolean initResult;
 
   public MaxAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    this.maxResult = Type.fromTsDataType(seriesDataType).getTsPrimitiveType();
+    this.type = Type.fromTsDataType(seriesDataType);
+    this.maxResult = type.getTsPrimitiveType();
+    this.valueUpdater = TypeServices.MAX_COLUMN_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -60,33 +67,7 @@ public class MaxAccumulator implements TableAccumulator {
   public void addInput(Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of MAX should be one column");
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        addBinaryInput(arguments[0], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
-    }
+    addInput(arguments[0], mask);
   }
 
   @Override
@@ -96,32 +77,8 @@ public class MaxAccumulator implements TableAccumulator {
         continue;
       }
 
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          updateIntMaxValue(argument.getInt(i));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          updateLongMaxValue(argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatMaxValue(argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleMaxValue(argument.getDouble(i));
-          break;
-        case STRING:
-        case TEXT:
-        case BLOB:
-          updateBinaryMaxValue(argument.getBinary(i));
-          break;
-        case BOOLEAN:
-          updateBooleanMaxValue(argument.getBoolean(i));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+      if (valueUpdater.update(maxResult, argument, i, initResult)) {
+        initResult = true;
       }
     }
   }
@@ -133,33 +90,7 @@ public class MaxAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(maxResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(maxResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(maxResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(maxResult.getDouble());
-        break;
-      case STRING:
-      case TEXT:
-      case BLOB:
-        columnBuilder.writeBinary(maxResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(maxResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, maxResult);
   }
 
   @Override
@@ -168,33 +99,7 @@ public class MaxAccumulator implements TableAccumulator {
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(maxResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(maxResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(maxResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(maxResult.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-        columnBuilder.writeBinary(maxResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(maxResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, maxResult);
   }
 
   @Override
@@ -241,7 +146,7 @@ public class MaxAccumulator implements TableAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
   }
 
@@ -251,6 +156,26 @@ public class MaxAccumulator implements TableAccumulator {
     this.maxResult.reset();
   }
 
+  private void addInput(Column valueColumn, AggregationMask mask) {
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        if (!valueColumn.isNull(i) && valueUpdater.update(maxResult, valueColumn, i, initResult)) {
+          initResult = true;
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!valueColumn.isNull(position)
+            && valueUpdater.update(maxResult, valueColumn, position, initResult)) {
+          initResult = true;
+        }
+      }
+    }
+  }
+
   private void addIntInput(Column valueColumn, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
index ef32bd28674..5002d72c713 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
@@ -19,6 +19,9 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.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;
@@ -38,12 +41,16 @@ public class MinAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(MinAccumulator.class);
   private final TSDataType seriesDataType;
+  private final Type type;
+  private final TypeServices.ColumnValueUpdater valueUpdater;
   private final TsPrimitiveType minResult;
   private boolean initResult;
 
   public MinAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    this.minResult = Type.fromTsDataType(seriesDataType).getTsPrimitiveType();
+    this.type = Type.fromTsDataType(seriesDataType);
+    this.minResult = type.getTsPrimitiveType();
+    this.valueUpdater = TypeServices.MIN_COLUMN_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -60,33 +67,7 @@ public class MinAccumulator implements TableAccumulator {
   public void addInput(Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of MIN should be one column");
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        addBinaryInput(arguments[0], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments[0], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
-    }
+    addInput(arguments[0], mask);
   }
 
   @Override
@@ -96,32 +77,8 @@ public class MinAccumulator implements TableAccumulator {
         continue;
       }
 
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          updateIntMinValue(argument.getInt(i));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          updateLongMinValue(argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatMinValue(argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleMinValue(argument.getDouble(i));
-          break;
-        case STRING:
-        case TEXT:
-        case BLOB:
-          updateBinaryMinValue(argument.getBinary(i));
-          break;
-        case BOOLEAN:
-          updateBooleanMinValue(argument.getBoolean(i));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+      if (valueUpdater.update(minResult, argument, i, initResult)) {
+        initResult = true;
       }
     }
   }
@@ -133,33 +90,7 @@ public class MinAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(minResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(minResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(minResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(minResult.getDouble());
-        break;
-      case STRING:
-      case TEXT:
-      case BLOB:
-        columnBuilder.writeBinary(minResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(minResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, minResult);
   }
 
   @Override
@@ -168,33 +99,7 @@ public class MinAccumulator implements TableAccumulator {
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(minResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(minResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(minResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(minResult.getDouble());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-        columnBuilder.writeBinary(minResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(minResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
-    }
+    type.write(columnBuilder, minResult);
   }
 
   @Override
@@ -241,7 +146,7 @@ public class MinAccumulator implements TableAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
   }
 
@@ -251,6 +156,26 @@ public class MinAccumulator implements TableAccumulator {
     this.minResult.reset();
   }
 
+  private void addInput(Column valueColumn, AggregationMask mask) {
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        if (!valueColumn.isNull(i) && valueUpdater.update(minResult, valueColumn, i, initResult)) {
+          initResult = true;
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!valueColumn.isNull(position)
+            && valueUpdater.update(minResult, valueColumn, position, initResult)) {
+          initResult = true;
+        }
+      }
+    }
+  }
+
   private void addIntInput(Column valueColumn, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/PercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/PercentileAccumulator.java
index 4a6b075d0c4..e625c2876aa 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/PercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/PercentileAccumulator.java
@@ -20,13 +20,16 @@
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
 import org.apache.iotdb.calc.execution.operator.source.relational.Percentile;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.exception.SemanticException;
 
 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.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -39,6 +42,8 @@ public class PercentileAccumulator implements TableAccumulator {
       RamUsageEstimator.shallowSizeOfInstance(PercentileAccumulator.class);
 
   private final TSDataType seriesDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
+  private final TypeServices.NumericResultWriter resultWriter;
   private Percentile percentile = new Percentile();
   // percentage is a query-level constant; it is read once from the first input/intermediate and
   // kept fixed afterwards, so it never gets reset to 0 by a later all-null batch.
@@ -52,6 +57,17 @@ public class PercentileAccumulator implements TableAccumulator {
       TSDataType seriesDataType, MemoryReservationManager memoryReservationManager) {
     this.seriesDataType = seriesDataType;
     this.memoryReservationManager = memoryReservationManager;
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueConverter =
+        TypeServices.PERCENTILE_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_PERCENTILE_AGGREGATION,
+                            seriesDataType)));
+    this.resultWriter = TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(type);
     updateMemoryReservation();
   }
 
@@ -69,30 +85,13 @@ public class PercentileAccumulator implements TableAccumulator {
   public void addInput(Column[] arguments, AggregationMask mask) {
     if (arguments.length != 2) {
       throw new SemanticException(
-          String.format("PERCENTILE requires 2 arguments, but got %d", arguments.length));
+          String.format(CalcMessages.PERCENTILE_REQUIRES_TWO_ARGUMENTS, arguments.length));
     }
     if (!percentageInitialized) {
       percentage = arguments[1].getDouble(0);
       percentageInitialized = true;
     }
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(arguments[0], mask);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], mask);
-        break;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        break;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in Percentile Aggregation: %s", seriesDataType));
-    }
+    addInput(arguments[0], mask);
     updateMemoryReservation();
   }
 
@@ -132,24 +131,7 @@ public class PercentileAccumulator implements TableAccumulator {
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt((int) result);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong((long) result);
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat((float) result);
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(result);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in PERCENTILE Aggregation: %s", seriesDataType));
-    }
+    resultWriter.write(columnBuilder, result);
   }
 
   @Override
@@ -159,7 +141,8 @@ public class PercentileAccumulator implements TableAccumulator {
 
   @Override
   public void addStatistics(Statistics[] statistics) {
-    throw new UnsupportedOperationException("PercentileAccumulator does not support statistics");
+    throw new UnsupportedOperationException(
+        CalcMessages.PERCENTILE_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS);
   }
 
   @Override
@@ -180,76 +163,13 @@ public class PercentileAccumulator implements TableAccumulator {
     previousPercentileSize = currentSize;
   }
 
-  private void addIntInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          percentile.addValue(column.getInt(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          percentile.addValue(column.getInt(position));
-        }
-      }
-    }
-  }
-
-  private void addLongInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          percentile.addValue(column.getLong(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          percentile.addValue(column.getLong(position));
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          percentile.addValue(column.getFloat(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          percentile.addValue(column.getFloat(position));
-        }
-      }
-    }
-  }
-
-  private void addDoubleInput(Column column, AggregationMask mask) {
+  private void addInput(Column column, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
     if (mask.isSelectAll()) {
       for (int i = 0; i < positionCount; i++) {
         if (!column.isNull(i)) {
-          percentile.addValue(column.getDouble(i));
+          percentile.addValue(valueConverter.convert(column, i));
         }
       }
     } else {
@@ -258,7 +178,7 @@ public class PercentileAccumulator implements TableAccumulator {
       for (int i = 0; i < positionCount; i++) {
         position = selectedPositions[i];
         if (!column.isNull(position)) {
-          percentile.addValue(column.getDouble(position));
+          percentile.addValue(valueConverter.convert(column, position));
         }
       }
     }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java
index 0c392ebaa2b..b375bf466fd 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java
@@ -18,11 +18,15 @@
  */
 package org.apache.iotdb.calc.execution.operator.source.relational.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.IntegerStatistics;
 import org.apache.tsfile.file.metadata.statistics.Statistics;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
@@ -32,11 +36,22 @@ public class SumAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(SumAccumulator.class);
   private final TSDataType argumentDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
   private double sumValue = 0;
   private boolean initResult = false;
 
   public SumAccumulator(TSDataType argumentDataType) {
     this.argumentDataType = argumentDataType;
+    Type type = Type.fromTsDataType(argumentDataType);
+    this.valueConverter =
+        TypeServices.AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_SUM_AGGREGATION,
+                            argumentDataType)));
   }
 
   @Override
@@ -52,59 +67,13 @@ public class SumAccumulator implements TableAccumulator {
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of SUM should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        addIntInput(arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in SUM Aggregation: %s", argumentDataType));
-    }
+    addInput(arguments[0], mask);
   }
 
   @Override
   public void removeInput(Column[] arguments) {
     checkArgument(arguments.length == 1, "argument of SUM should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        removeIntInput(arguments[0]);
-        return;
-      case INT64:
-        removeLongInput(arguments[0]);
-        return;
-      case FLOAT:
-        removeFloatInput(arguments[0]);
-        return;
-      case DOUBLE:
-        removeDoubleInput(arguments[0]);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in SUM Aggregation: %s", argumentDataType));
-    }
+    removeInput(arguments[0]);
   }
 
   @Override
@@ -167,31 +136,7 @@ public class SumAccumulator implements TableAccumulator {
     return true;
   }
 
-  private void addIntInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      int count = column.getPositionCount();
-      for (int i = 0; i < count; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          sumValue += column.getInt(i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          sumValue += column.getInt(position);
-        }
-      }
-    }
-  }
-
-  private void addLongInput(Column column, AggregationMask mask) {
+  private void addInput(Column column, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
     if (mask.isSelectAll()) {
@@ -199,7 +144,7 @@ public class SumAccumulator implements TableAccumulator {
       for (int i = 0; i < count; i++) {
         if (!column.isNull(i)) {
           initResult = true;
-          sumValue += column.getLong(i);
+          sumValue += valueConverter.convert(column, i);
         }
       }
     } else {
@@ -209,92 +154,17 @@ public class SumAccumulator implements TableAccumulator {
         position = selectedPositions[i];
         if (!column.isNull(position)) {
           initResult = true;
-          sumValue += column.getLong(position);
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      int count = column.getPositionCount();
-      for (int i = 0; i < count; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          sumValue += column.getFloat(i);
+          sumValue += valueConverter.convert(column, position);
         }
       }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          sumValue += column.getFloat(position);
-        }
-      }
-    }
-  }
-
-  private void addDoubleInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      int count = column.getPositionCount();
-      for (int i = 0; i < count; i++) {
-        if (!column.isNull(i)) {
-          initResult = true;
-          sumValue += column.getDouble(i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult = true;
-          sumValue += column.getDouble(position);
-        }
-      }
-    }
-  }
-
-  private void removeIntInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        sumValue -= column.getInt(i);
-      }
-    }
-  }
-
-  private void removeLongInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        sumValue -= column.getLong(i);
-      }
-    }
-  }
-
-  private void removeFloatInput(Column column) {
-    int count = column.getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (!column.isNull(i)) {
-        sumValue -= column.getFloat(i);
-      }
     }
   }
 
-  private void removeDoubleInput(Column column) {
+  private void removeInput(Column column) {
     int count = column.getPositionCount();
     for (int i = 0; i < count; i++) {
       if (!column.isNull(i)) {
-        sumValue -= column.getDouble(i);
+        sumValue -= valueConverter.convert(column, i);
       }
     }
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java
index 74d9a31dc7c..98005bb18ea 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java
@@ -19,11 +19,12 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
+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.block.TsBlockBuilder;
 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;
@@ -31,16 +32,14 @@ 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.TsPrimitiveType;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
-
-import java.util.Collections;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.calcTypeSize;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeValue;
 
 /** max(x,y) returns the value of x associated with the maximum value of y over all input values. */
-public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
+public abstract class TableMaxMinByBaseAccumulator
+    implements TableAccumulator, TypeServices.MaxMinByValueUpdater {
 
   protected final TSDataType xDataType;
 
@@ -49,52 +48,29 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
   private final TsPrimitiveType yExtremeValue;
 
   private final TsPrimitiveType xResult;
+  private final Type xType;
+  private final TypeServices.PrimitiveColumnValueSetter xValueSetter;
 
   private boolean xNull = true;
 
   private boolean initResult;
 
-  public static final String UNSUPPORTED_TYPE_MESSAGE =
-      "Unsupported data type in MAX_BY/MIN_BY: %s";
-
   protected TableMaxMinByBaseAccumulator(TSDataType xDataType, TSDataType yDataType) {
     this.xDataType = xDataType;
     this.yDataType = yDataType;
-    this.xResult = Type.fromTsDataType(xDataType).getTsPrimitiveType();
+    this.xType = Type.fromTsDataType(xDataType);
+    this.xResult = xType.getTsPrimitiveType();
     this.yExtremeValue = Type.fromTsDataType(yDataType).getTsPrimitiveType();
+    this.xValueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(xType);
   }
 
   // Column should be like: | x | y |
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 2, "Length of input Column[] for MAX_BY/MIN_BY should be 2");
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(arguments, mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments, mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments, mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments, mask);
-        return;
-      case STRING:
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(arguments, mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(arguments, mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(String.format(UNSUPPORTED_TYPE_MESSAGE, yDataType));
-    }
+    TypeServices.MAX_MIN_BY_INPUT_SERVICE
+        .call(Type.fromTsDataType(yDataType))
+        .add(arguments, mask, this);
   }
 
   @Override
@@ -155,28 +131,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     return false;
   }
 
-  private void addIntInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateIntResult(column[1].getInt(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateIntResult(column[1].getInt(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateIntResult(int yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateInt(int yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getInt())) {
       initResult = true;
       yExtremeValue.setInt(yValue);
@@ -184,28 +140,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     }
   }
 
-  private void addLongInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateLongResult(column[1].getLong(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateLongResult(column[1].getLong(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateLongResult(long yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateLong(long yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getLong())) {
       initResult = true;
       yExtremeValue.setLong(yValue);
@@ -213,28 +149,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     }
   }
 
-  private void addFloatInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateFloatResult(column[1].getFloat(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateFloatResult(column[1].getFloat(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateFloatResult(float yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateFloat(float yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getFloat())) {
       initResult = true;
       yExtremeValue.setFloat(yValue);
@@ -242,28 +158,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     }
   }
 
-  private void addDoubleInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateDoubleResult(column[1].getDouble(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateDoubleResult(column[1].getDouble(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateDoubleResult(double yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateDouble(double yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getDouble())) {
       initResult = true;
       yExtremeValue.setDouble(yValue);
@@ -271,28 +167,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     }
   }
 
-  private void addBinaryInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateBinaryResult(column[1].getBinary(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateBinaryResult(column[1].getBinary(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateBinaryResult(Binary yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateBinary(Binary yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getBinary())) {
       initResult = true;
       yExtremeValue.setBinary(yValue);
@@ -300,28 +176,8 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
     }
   }
 
-  private void addBooleanInput(Column[] column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column[1].isNull(i)) {
-          updateBooleanResult(column[1].getBoolean(i), column[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column[1].isNull(position)) {
-          updateBooleanResult(column[1].getBoolean(position), column[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateBooleanResult(boolean yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateBoolean(boolean yValue, Column xColumn, int xIndex) {
     if (!initResult || check(yValue, yExtremeValue.getBoolean())) {
       initResult = true;
       yExtremeValue.setBoolean(yValue);
@@ -334,33 +190,7 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
       columnBuilder.appendNull();
       return;
     }
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xResult.getInt());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xResult.getLong());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xResult.getFloat());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xResult.getDouble());
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        columnBuilder.writeBinary(xResult.getBinary());
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xResult.getBoolean());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(String.format(UNSUPPORTED_TYPE_MESSAGE, xDataType));
-    }
+    xType.write(columnBuilder, xResult);
   }
 
   private void updateX(Column xColumn, int xIndex) {
@@ -368,34 +198,7 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
       xNull = true;
     } else {
       xNull = false;
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          xResult.setInt(xColumn.getInt(xIndex));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          xResult.setLong(xColumn.getLong(xIndex));
-          break;
-        case FLOAT:
-          xResult.setFloat(xColumn.getFloat(xIndex));
-          break;
-        case DOUBLE:
-          xResult.setDouble(xColumn.getDouble(xIndex));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          xResult.setBinary(xColumn.getBinary(xIndex));
-          break;
-        case BOOLEAN:
-          xResult.setBoolean(xColumn.getBoolean(xIndex));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(UNSUPPORTED_TYPE_MESSAGE, xDataType));
-      }
+      xValueSetter.set(xResult, xColumn, xIndex);
     }
   }
 
@@ -421,97 +224,11 @@ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator {
   }
 
   private void updateFromBytesIntermediateInput(byte[] bytes) {
-    int offset = 0;
-    // Use Column to store x value
-    TsBlockBuilder builder = new TsBlockBuilder(Collections.singletonList(xDataType));
-    ColumnBuilder columnBuilder = builder.getValueColumnBuilders()[0];
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        int intMaxVal = BytesUtils.bytesToInt(bytes, offset);
-        offset += Integer.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateIntResult(intMaxVal, columnBuilder.build(), 0);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        long longMaxVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-        offset += Long.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateLongResult(longMaxVal, columnBuilder.build(), 0);
-        break;
-      case FLOAT:
-        float floatMaxVal = BytesUtils.bytesToFloat(bytes, offset);
-        offset += Float.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateFloatResult(floatMaxVal, columnBuilder.build(), 0);
-        break;
-      case DOUBLE:
-        double doubleMaxVal = BytesUtils.bytesToDouble(bytes, offset);
-        offset += Long.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateDoubleResult(doubleMaxVal, columnBuilder.build(), 0);
-        break;
-      case STRING:
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-        int length = BytesUtils.bytesToInt(bytes, offset);
-        offset += Integer.BYTES;
-        Binary binaryMaxVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-        offset += length;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateBinaryResult(binaryMaxVal, columnBuilder.build(), 0);
-        break;
-      case BOOLEAN:
-        boolean booleanMaxVal = BytesUtils.bytesToBool(bytes, offset);
-        offset += 1;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateBooleanResult(booleanMaxVal, columnBuilder.build(), 0);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(String.format(UNSUPPORTED_TYPE_MESSAGE, yDataType));
-    }
-  }
-
-  private void readXFromBytesIntermediateInput(
-      byte[] bytes, int offset, ColumnBuilder columnBuilder) {
-    boolean isXNull = BytesUtils.bytesToBool(bytes, offset);
-    offset += 1;
-    if (isXNull) {
-      columnBuilder.appendNull();
-    } else {
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(BytesUtils.bytesToInt(bytes, offset));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(BytesUtils.bytesToLongFromOffset(bytes, 8, offset));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(BytesUtils.bytesToFloat(bytes, offset));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(BytesUtils.bytesToDouble(bytes, offset));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          columnBuilder.writeBinary(new Binary(BytesUtils.subBytes(bytes, offset, length)));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(BytesUtils.bytesToBool(bytes, offset));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(UNSUPPORTED_TYPE_MESSAGE, xDataType));
-      }
-    }
+    // Use a one-row column so the shared updater applies the same null and x-value semantics as
+    // regular input processing.
+    TypeServices.MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE
+        .call(Type.fromTsDataType(yDataType))
+        .update(bytes, xType, xType.createColumnBuilder(null, 1), this);
   }
 
   /**
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java
index c7d1a130391..0ab15c956e4 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java
@@ -19,6 +19,8 @@
 
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation;
 
+import org.apache.iotdb.calc.i18n.CalcMessages;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 
 import org.apache.tsfile.block.column.Column;
@@ -28,6 +30,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -36,10 +39,6 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.UNSUPPORTED_TYPE_MESSAGE;
-import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeBinaryValue;
-import static org.apache.tsfile.utils.BytesUtils.bytesToBool;
-import static org.apache.tsfile.utils.BytesUtils.bytesToLongFromOffset;
 
 public class TableModeAccumulator implements TableAccumulator {
 
@@ -48,44 +47,17 @@ public class TableModeAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(TableModeAccumulator.class);
   private final TSDataType seriesDataType;
-
-  private Map<Boolean, Long> booleanCountMap;
-  private Map<Integer, Long> intCountMap;
-  private Map<Float, Long> floatCountMap;
-  private Map<Long, Long> longCountMap;
-  private Map<Double, Long> doubleCountMap;
-  private Map<Binary, Long> binaryCountMap;
+  private final TypeServices.ModeValueService valueService;
+  private final Map<Object, Long> countMap = new HashMap<>();
 
   private long nullCount;
 
   public TableModeAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case BOOLEAN:
-        booleanCountMap = new HashMap<>();
-        break;
-      case INT32:
-      case DATE:
-        intCountMap = new HashMap<>();
-        break;
-      case FLOAT:
-        floatCountMap = new HashMap<>();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        longCountMap = new HashMap<>();
-        break;
-      case DOUBLE:
-        doubleCountMap = new HashMap<>();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        binaryCountMap = new HashMap<>();
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    this.valueService = TypeServices.MODE_VALUE_SERVICE.call(Type.fromTsDataType(seriesDataType));
+    if (seriesDataType == TSDataType.OBJECT) {
+      throw new UnsupportedOperationException(
+          String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MODE_AGGREGATION, seriesDataType));
     }
   }
 
@@ -101,63 +73,30 @@ public class TableModeAccumulator implements TableAccumulator {
 
   @Override
   public void addInput(Column[] arguments, AggregationMask mask) {
-    switch (seriesDataType) {
-      case BOOLEAN:
-        addBooleanInput(arguments[0], mask);
-        break;
-      case INT32:
-      case DATE:
-        addIntInput(arguments[0], mask);
-        break;
-      case FLOAT:
-        addFloatInput(arguments[0], mask);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments[0], mask);
-        break;
-      case DOUBLE:
-        addDoubleInput(arguments[0], mask);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        addBinaryInput(arguments[0], mask);
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    Column column = arguments[0];
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        addValue(column, i);
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        addValue(column, selectedPositions[i]);
+      }
     }
   }
 
   @Override
   public void removeInput(Column[] arguments) {
-    switch (seriesDataType) {
-      case BOOLEAN:
-        removeBooleanInput(arguments[0]);
-        break;
-      case INT32:
-      case DATE:
-        removeIntInput(arguments[0]);
-        break;
-      case FLOAT:
-        removeFloatInput(arguments[0]);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        removeLongInput(arguments[0]);
-        break;
-      case DOUBLE:
-        removeDoubleInput(arguments[0]);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        removeBinaryInput(arguments[0]);
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    Column column = arguments[0];
+    for (int i = 0; i < column.getPositionCount(); i++) {
+      if (column.isNull(i)) {
+        nullCount--;
+      } else {
+        Object key = valueService.getValue(column, i);
+        countMap.computeIfPresent(key, (k, count) -> count - 1);
+      }
     }
   }
 
@@ -190,93 +129,16 @@ public class TableModeAccumulator implements TableAccumulator {
 
   @Override
   public void evaluateFinal(ColumnBuilder columnBuilder) {
-    switch (seriesDataType) {
-      case BOOLEAN:
-        if (booleanCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          // must be present
-          Map.Entry<Boolean, Long> maxEntry =
-              booleanCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeBoolean(maxEntry.getKey());
-          }
-        }
-        break;
-      case INT32:
-      case DATE:
-        if (intCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          Map.Entry<Integer, Long> maxEntry =
-              intCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeInt(maxEntry.getKey());
-          }
-        }
-        break;
-      case FLOAT:
-        if (floatCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          Map.Entry<Float, Long> maxEntry =
-              floatCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeFloat(maxEntry.getKey());
-          }
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        if (longCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          Map.Entry<Long, Long> maxEntry =
-              longCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeLong(maxEntry.getKey());
-          }
-        }
-        break;
-      case DOUBLE:
-        if (doubleCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          Map.Entry<Double, Long> maxEntry =
-              doubleCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeDouble(maxEntry.getKey());
-          }
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        if (binaryCountMap.isEmpty()) {
-          columnBuilder.appendNull();
-        } else {
-          Map.Entry<Binary, Long> maxEntry =
-              binaryCountMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
-          if (maxEntry.getValue() < nullCount) {
-            columnBuilder.appendNull();
-          } else {
-            columnBuilder.writeBinary(maxEntry.getKey());
-          }
-        }
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    if (countMap.isEmpty()) {
+      columnBuilder.appendNull();
+      return;
+    }
+    Map.Entry<Object, Long> maxEntry =
+        countMap.entrySet().stream().max(Map.Entry.comparingByValue()).get();
+    if (maxEntry.getValue() < nullCount) {
+      columnBuilder.appendNull();
+    } else {
+      valueService.write(columnBuilder, maxEntry.getKey());
     }
   }
 
@@ -292,24 +154,7 @@ public class TableModeAccumulator implements TableAccumulator {
 
   @Override
   public void reset() {
-    if (booleanCountMap != null) {
-      booleanCountMap.clear();
-    }
-    if (intCountMap != null) {
-      intCountMap.clear();
-    }
-    if (floatCountMap != null) {
-      floatCountMap.clear();
-    }
-    if (longCountMap != null) {
-      longCountMap.clear();
-    }
-    if (doubleCountMap != null) {
-      doubleCountMap.clear();
-    }
-    if (binaryCountMap != null) {
-      binaryCountMap.clear();
-    }
+    countMap.clear();
     nullCount = 0;
   }
 
@@ -320,424 +165,53 @@ public class TableModeAccumulator implements TableAccumulator {
 
   // haveNull | nullCount (optional) | countMap
   private byte[] serializeCountMap() {
-    byte[] bytes;
     int offset = 1 + (nullCount == 0 ? 0 : Long.BYTES);
-    ;
-
-    switch (seriesDataType) {
-      case BOOLEAN:
-        bytes = new byte[offset + 4 + (1 + 8) * booleanCountMap.size()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(booleanCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Boolean, Long> entry : booleanCountMap.entrySet()) {
-          BytesUtils.boolToBytes(entry.getKey(), bytes, offset);
-          offset += 1;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      case INT32:
-      case DATE:
-        bytes = new byte[offset + 4 + (4 + 8) * intCountMap.size()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(intCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Integer, Long> entry : intCountMap.entrySet()) {
-          BytesUtils.intToBytes(entry.getKey(), bytes, offset);
-          offset += 4;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      case FLOAT:
-        bytes = new byte[offset + 4 + (4 + 8) * floatCountMap.size()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(floatCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Float, Long> entry : floatCountMap.entrySet()) {
-          BytesUtils.floatToBytes(entry.getKey(), bytes, offset);
-          offset += 4;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        bytes = new byte[offset + 4 + (8 + 8) * longCountMap.size()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(longCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Long, Long> entry : longCountMap.entrySet()) {
-          BytesUtils.longToBytes(entry.getKey(), bytes, offset);
-          offset += 8;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      case DOUBLE:
-        bytes = new byte[offset + 4 + (8 + 8) * doubleCountMap.size()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(doubleCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Double, Long> entry : doubleCountMap.entrySet()) {
-          BytesUtils.doubleToBytes(entry.getKey(), bytes, offset);
-          offset += 8;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        bytes =
-            new byte
-                [offset
-                    + 4
-                    + (8 + 4) * binaryCountMap.size()
-                    + binaryCountMap.keySet().stream()
-                        .mapToInt(key -> key.getValues().length)
-                        .sum()];
-        BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
-        if (nullCount != 0) {
-          BytesUtils.longToBytes(nullCount, bytes, 1);
-        }
-        BytesUtils.intToBytes(binaryCountMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<Binary, Long> entry : binaryCountMap.entrySet()) {
-          Binary binary = entry.getKey();
-          serializeBinaryValue(binary, bytes, offset);
-          offset += (4 + binary.getLength());
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += 8;
-        }
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    int valueSize =
+        seriesDataType.isBinary()
+            ? countMap.keySet().stream().mapToInt(valueService::calcTypeSize).sum()
+            : countMap.size() * seriesDataType.getDataTypeSize();
+    byte[] bytes = new byte[offset + Integer.BYTES + valueSize + Long.BYTES * countMap.size()];
+    BytesUtils.boolToBytes(nullCount != 0, bytes, 0);
+    if (nullCount != 0) {
+      BytesUtils.longToBytes(nullCount, bytes, 1);
+    }
+    BytesUtils.intToBytes(countMap.size(), bytes, offset);
+    offset += Integer.BYTES;
+    for (Map.Entry<Object, Long> entry : countMap.entrySet()) {
+      Object key = entry.getKey();
+      offset += valueService.serialize(key, bytes, offset);
+      BytesUtils.longToBytes(entry.getValue(), bytes, offset);
+      offset += Long.BYTES;
     }
-
     return bytes;
   }
 
   private void deserializeAndMergeCountMap(byte[] bytes) {
     int offset = 0;
-    if (bytesToBool(bytes, 0)) {
-      nullCount += bytesToLongFromOffset(bytes, Long.BYTES, 1);
+    if (BytesUtils.bytesToBool(bytes, 0)) {
+      nullCount += BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 1);
       offset += Long.BYTES;
     }
     offset++;
     int size = BytesUtils.bytesToInt(bytes, offset);
-    offset += 4;
-
-    switch (seriesDataType) {
-      case BOOLEAN:
-        for (int i = 0; i < size; i++) {
-          boolean key = BytesUtils.bytesToBool(bytes, offset);
-          offset += 1;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          booleanCountMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case INT32:
-      case DATE:
-        for (int i = 0; i < size; i++) {
-          int key = BytesUtils.bytesToInt(bytes, offset);
-          offset += 4;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          intCountMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < size; i++) {
-          float key = BytesUtils.bytesToFloat(bytes, offset);
-          offset += 4;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          floatCountMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        for (int i = 0; i < size; i++) {
-          long key = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += 8;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          longCountMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case DOUBLE:
-        for (int i = 0; i < size; i++) {
-          double key = BytesUtils.bytesToDouble(bytes, offset);
-          offset += 8;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          doubleCountMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-        for (int i = 0; i < size; i++) {
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += 4;
-          Binary binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          offset += length;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, 8, offset);
-          offset += 8;
-          binaryCountMap.compute(binaryVal, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
-    }
-  }
-
-  private void addBooleanInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          booleanCountMap.compute(column.getBoolean(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(booleanCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          booleanCountMap.compute(column.getBoolean(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(booleanCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void addIntInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          intCountMap.compute(column.getInt(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(intCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          intCountMap.compute(column.getInt(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(intCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          floatCountMap.compute(column.getFloat(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(floatCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          floatCountMap.compute(column.getFloat(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(floatCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void addLongInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          longCountMap.compute(column.getLong(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(longCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          longCountMap.compute(column.getLong(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(longCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void addDoubleInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          doubleCountMap.compute(column.getDouble(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(doubleCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          doubleCountMap.compute(column.getDouble(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(doubleCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void addBinaryInput(Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          binaryCountMap.compute(column.getBinary(i), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(binaryCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          binaryCountMap.compute(column.getBinary(position), (k, v) -> v == null ? 1 : v + 1);
-          checkMapSize(binaryCountMap.size());
-        } else {
-          nullCount++;
-        }
-      }
-    }
-  }
-
-  private void removeBooleanInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        boolean key = column.getBoolean(i);
-        booleanCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
-    }
-  }
-
-  private void removeIntInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        int key = column.getInt(i);
-        intCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
-    }
-  }
-
-  private void removeFloatInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        float key = column.getFloat(i);
-        floatCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
-    }
-  }
-
-  private void removeLongInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        long key = column.getLong(i);
-        longCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
-    }
-  }
-
-  private void removeDoubleInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        double key = column.getDouble(i);
-        doubleCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
+    offset += Integer.BYTES;
+    for (int i = 0; i < size; i++) {
+      Object key = valueService.deserialize(bytes, offset);
+      offset += valueService.calcTypeSize(key);
+      long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
+      offset += Long.BYTES;
+      countMap.compute(key, (k, v) -> v == null ? count : v + count);
     }
   }
 
-  private void removeBinaryInput(Column column) {
-    for (int i = 0; i < column.getPositionCount(); i++) {
-      if (!column.isNull(i)) {
-        Binary key = column.getBinary(i);
-        binaryCountMap.compute(key, (k, count) -> count - 1);
-      } else {
-        nullCount--;
-      }
+  private void addValue(Column column, int position) {
+    if (column.isNull(position)) {
+      nullCount++;
+      return;
     }
+    countMap.compute(
+        valueService.getValue(column, position), (key, count) -> count == null ? 1L : count + 1);
+    checkMapSize(countMap.size());
   }
 
   private void checkMapSize(int size) {
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/Utils.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/Utils.java
index 78a1875ba79..8bd550bbdd5 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/Utils.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/Utils.java
@@ -30,8 +30,6 @@ import static org.apache.tsfile.enums.TSDataType.STRING;
 import static org.apache.tsfile.enums.TSDataType.TEXT;
 
 public class Utils {
-  public static final String UNSUPPORTED_TYPE_MESSAGE = "Unsupported data type : %s";
-
   private Utils() {}
 
   public static void serializeValue(
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
index 0c5fb7d789f..7f6834f0004 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
@@ -22,10 +22,13 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.g
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.TDigest;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.TDigestBigArray;
+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.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -38,11 +41,14 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedApproxPercentileAccumulator.class);
   protected final TSDataType seriesDataType;
+  private final TypeServices.NumericResultWriter resultWriter;
   protected double percentage;
   protected final TDigestBigArray array = new TDigestBigArray();
 
   AbstractGroupedApproxPercentileAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
+    this.resultWriter =
+        TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(Type.fromTsDataType(seriesDataType));
   }
 
   @Override
@@ -64,7 +70,7 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
     } else {
       throw new IllegalArgumentException(
           String.format(
-              "APPROX_PERCENTILE requires 2 or 3 arguments, but got %d", arguments.length));
+              CalcMessages.APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS, arguments.length));
     }
 
     switch (seriesDataType) {
@@ -84,7 +90,8 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
       default:
         throw new UnSupportedDataTypeException(
             String.format(
-                "Unsupported data type in APPROX_PERCENTILE Aggregation: %s", seriesDataType));
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
+                seriesDataType));
     }
   }
 
@@ -120,25 +127,7 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt((int) result);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong((long) result);
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat((float) result);
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(result);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                "Unsupported data type in APPROX_PERCENTILE Aggregation: %s", seriesDataType));
-    }
+    resultWriter.write(columnBuilder, result);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedApproxCountDistinctAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedApproxCountDistinctAccumulator.java
index 115ddfe3a64..a02f39d85e6 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedApproxCountDistinctAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedApproxCountDistinctAccumulator.java
@@ -23,13 +23,14 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Ag
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLogStateFactory;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.HyperLogLogBigArray;
+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.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.RamUsageEstimator;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog.DEFAULT_STANDARD_ERROR;
 
@@ -37,12 +38,15 @@ public class GroupedApproxCountDistinctAccumulator implements GroupedAccumulator
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedApproxCountDistinctAccumulator.class);
   private final TSDataType seriesDataType;
+  private final TypeServices.HyperLogLogColumnAdder valueAdder;
 
   private final HyperLogLogStateFactory.GroupedHyperLogLogState state =
       HyperLogLogStateFactory.createGroupedState();
 
   public GroupedApproxCountDistinctAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
+    this.valueAdder =
+        TypeServices.HYPER_LOG_LOG_COLUMN_ADDER_SERVICE.call(Type.fromTsDataType(seriesDataType));
   }
 
   @Override
@@ -62,34 +66,24 @@ public class GroupedApproxCountDistinctAccumulator implements GroupedAccumulator
         arguments.length == 1 ? DEFAULT_STANDARD_ERROR : arguments[1].getDouble(0);
     HyperLogLogBigArray hlls = getOrCreateHyperLogLog(state);
 
-    switch (seriesDataType) {
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], mask, hlls, maxStandardError);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                "Unsupported data type in APPROX_COUNT_DISTINCT Aggregation: %s", seriesDataType));
+    Column valueColumn = arguments[0];
+    int positionCount = mask.getPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        HyperLogLog hll = hlls.get(groupIds[i], maxStandardError);
+        if (!valueColumn.isNull(i)) {
+          valueAdder.add(hll, valueColumn, i);
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        HyperLogLog hll = hlls.get(groupIds[position], maxStandardError);
+        if (!valueColumn.isNull(position)) {
+          valueAdder.add(hll, valueColumn, position);
+        }
+      }
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java
index 0f23f7c913b..7dfcdeece24 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java
@@ -21,6 +21,8 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.g
 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;
@@ -28,6 +30,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -41,11 +44,22 @@ public class GroupedAvgAccumulator implements GroupedAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedAvgAccumulator.class);
   private final TSDataType argumentDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
   private final LongBigArray countValues = new LongBigArray();
   private final DoubleBigArray sumValues = new DoubleBigArray();
 
   public GroupedAvgAccumulator(TSDataType argumentDataType) {
     this.argumentDataType = argumentDataType;
+    Type type = Type.fromTsDataType(argumentDataType);
+    this.valueConverter =
+        TypeServices.AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_AGGREGATION_AVG,
+                            argumentDataType)));
   }
 
   @Override
@@ -62,30 +76,7 @@ public class GroupedAvgAccumulator implements GroupedAccumulator {
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of AVG should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        addIntInput(groupIds, arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(groupIds, arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in AVG Aggregation: %s", argumentDataType));
-    }
+    addInput(groupIds, arguments[0], mask);
   }
 
   @Override
@@ -127,89 +118,14 @@ public class GroupedAvgAccumulator implements GroupedAccumulator {
     return bytes;
   }
 
-  private void addIntInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          countValues.increment(groupIds[i]);
-          sumValues.add(groupIds[i], column.getInt(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      int groupId;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        groupId = groupIds[position];
-        if (!column.isNull(position)) {
-          countValues.increment(groupId);
-          sumValues.add(groupId, column.getInt(position));
-        }
-      }
-    }
-  }
-
-  private void addLongInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          countValues.increment(groupIds[i]);
-          sumValues.add(groupIds[i], column.getLong(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      int groupId;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        groupId = groupIds[position];
-        if (!column.isNull(position)) {
-          countValues.increment(groupId);
-          sumValues.add(groupId, column.getLong(position));
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          countValues.increment(groupIds[i]);
-          sumValues.add(groupIds[i], column.getFloat(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      int groupId;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        groupId = groupIds[position];
-        if (!column.isNull(position)) {
-          countValues.increment(groupId);
-          sumValues.add(groupId, column.getFloat(position));
-        }
-      }
-    }
-  }
-
-  private void addDoubleInput(int[] groupIds, Column column, AggregationMask mask) {
+  private void addInput(int[] groupIds, Column column, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
     if (mask.isSelectAll()) {
       for (int i = 0; i < positionCount; i++) {
         if (!column.isNull(i)) {
           countValues.increment(groupIds[i]);
-          sumValues.add(groupIds[i], column.getDouble(i));
+          sumValues.add(groupIds[i], valueConverter.convert(column, i));
         }
       }
     } else {
@@ -221,7 +137,7 @@ public class GroupedAvgAccumulator implements GroupedAccumulator {
         groupId = groupIds[position];
         if (!column.isNull(position)) {
           countValues.increment(groupId);
-          sumValues.add(groupId, column.getDouble(position));
+          sumValues.add(groupId, valueConverter.convert(column, position));
         }
       }
     }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
index b4102de7aa0..9735d49066e 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -68,7 +69,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
       case TIMESTAMP:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type EXTREME Aggregation: %s", seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
   }
 
@@ -99,7 +101,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
 
     return INSTANCE_SIZE + valuesSize;
@@ -130,7 +133,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
       case BOOLEAN:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
   }
 
@@ -159,7 +163,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
       case TIMESTAMP:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type EXTREME Aggregation: %s", seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
   }
 
@@ -193,7 +198,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
         case TIMESTAMP:
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -225,7 +231,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
         case TIMESTAMP:
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -257,7 +264,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
         case TIMESTAMP:
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -290,7 +298,8 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
       case TIMESTAMP:
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in EXTREME Aggregation: %s", seriesDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
index b0c52d52501..6090aea3075 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -88,7 +89,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -121,7 +122,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
 
     return INSTANCE_SIZE
@@ -162,7 +163,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -195,7 +196,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -277,7 +278,8 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -327,7 +329,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -365,7 +367,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -427,7 +429,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
         return bytes;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
index 8364971fcea..691e98e2d4f 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -97,7 +98,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -130,7 +131,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
 
     return INSTANCE_SIZE
@@ -173,7 +174,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -212,7 +213,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -247,7 +248,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -338,7 +339,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
       }
     }
   }
@@ -397,7 +398,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
           return bytes;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
       }
     }
     return bytes;
@@ -424,7 +425,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         return 1;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -461,7 +462,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in FIRST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
index c5696464da4..2fec637c093 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -88,7 +89,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -121,7 +122,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
 
     return INSTANCE_SIZE
@@ -162,7 +163,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -195,7 +196,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -277,7 +278,8 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -327,7 +329,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -365,7 +367,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
@@ -427,7 +429,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
         return bytes;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
index ab6ddae16cc..62e6e995b0d 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -97,7 +98,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -130,7 +131,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
 
     return INSTANCE_SIZE
@@ -173,7 +174,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -212,7 +213,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -247,7 +248,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -338,7 +339,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
       }
     }
   }
@@ -396,7 +397,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
           return bytes;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
       }
     }
     return bytes;
@@ -423,7 +424,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         return 1;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -460,7 +461,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in LAST_BY Aggregation: %s", xDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
index 119501e4213..baea2267935 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -77,7 +78,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
   }
 
@@ -110,7 +111,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
 
     return INSTANCE_SIZE + valuesSize + inits.sizeOf();
@@ -145,7 +146,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
   }
 
@@ -178,7 +179,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
   }
 
@@ -216,7 +217,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -253,7 +254,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -289,7 +290,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -326,7 +327,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
index d21634698dd..a8007085669 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
@@ -210,7 +211,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
 
     switch (yDataType) {
@@ -239,7 +241,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
 
     return INSTANCE_SIZE + valuesSize + inits.sizeOf() + xNulls.sizeOf();
@@ -275,7 +278,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
     switch (yDataType) {
       case INT32:
@@ -303,7 +307,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -340,7 +345,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
 
     switch (yDataType) {
@@ -369,7 +375,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", yDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
     }
   }
 
@@ -401,7 +408,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", yDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
     }
   }
 
@@ -656,7 +664,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
     }
   }
 
@@ -690,7 +699,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
           xBooleanValues.set(groupId, xColumn.getBoolean(xIndex));
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
       }
     }
   }
@@ -777,7 +787,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", yDataType));
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
     }
   }
 
@@ -816,7 +827,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MAX_BY/MIN_BY Aggregation: %s", xDataType));
+              String.format(
+                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
       }
     }
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
index 1993e2142fc..9eb335c6f47 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
@@ -77,7 +78,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
   }
 
@@ -110,7 +111,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
         break;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
 
     return INSTANCE_SIZE + valuesSize + inits.sizeOf();
@@ -145,7 +146,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
   }
 
@@ -178,7 +179,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
   }
 
@@ -216,7 +217,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -253,7 +254,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -289,7 +290,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
           break;
         default:
           throw new UnSupportedDataTypeException(
-              String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
       }
     }
   }
@@ -326,7 +327,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
         return;
       default:
         throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in MIN Aggregation: %s", seriesDataType));
+            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java
index b94091a5943..74b0b5adf76 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java
@@ -36,12 +36,11 @@ import org.apache.tsfile.utils.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 
+import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.UNSUPPORTED_TYPE_MESSAGE;
-import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeBinaryValue;
 import static org.apache.tsfile.utils.BytesUtils.bytesToBool;
 import static org.apache.tsfile.utils.BytesUtils.bytesToLongFromOffset;
 
@@ -52,6 +51,7 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedModeAccumulator.class);
   private final TSDataType seriesDataType;
+  private final Type type;
 
   private final MapBigArray countMaps = new MapBigArray();
 
@@ -59,6 +59,7 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
 
   public GroupedModeAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
+    this.type = Type.fromTsDataType(seriesDataType);
   }
 
   @Override
@@ -74,34 +75,7 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    switch (seriesDataType) {
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], mask);
-        break;
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], mask);
-        break;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], mask);
-        break;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], mask);
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
-    }
+    addInput(groupIds, arguments[0], mask);
   }
 
   @Override
@@ -146,34 +120,7 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case BOOLEAN:
-        columnBuilder.writeBoolean(maxEntry.getKey().getBoolean());
-        break;
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(maxEntry.getKey().getInt());
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(maxEntry.getKey().getFloat());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(maxEntry.getKey().getLong());
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(maxEntry.getKey().getDouble());
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        columnBuilder.writeBinary(maxEntry.getKey().getBinary());
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
-    }
+    type.write(columnBuilder, maxEntry.getKey());
   }
 
   @Override
@@ -185,121 +132,52 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
     nullCounts.reset();
   }
 
+  private void addInput(int[] groupIds, Column column, AggregationMask mask) {
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        addValue(groupIds[i], column, i);
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        addValue(groupIds[position], column, position);
+      }
+    }
+  }
+
+  private void addValue(int groupId, Column column, int position) {
+    HashMap<TsPrimitiveType, Long> countMap = countMaps.get(groupId);
+    if (column.isNull(position)) {
+      nullCounts.increment(groupId);
+    } else {
+      countMap.compute(
+          column.getTsPrimitiveType(position), (key, count) -> count == null ? 1L : count + 1);
+      checkMapSize(countMap.size());
+    }
+  }
+
   // haveNull | nullCount (optional) | countMap
   private byte[] serializeCountMap(int groupId) {
-    byte[] bytes;
     int offset = 1 + (nullCounts.get(groupId) == 0 ? 0 : Long.BYTES);
     HashMap<TsPrimitiveType, Long> countMap = countMaps.get(groupId);
-
-    switch (seriesDataType) {
-      case BOOLEAN:
-        bytes = new byte[offset + Integer.BYTES + (1 + Long.BYTES) * countMap.size()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += 4;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          BytesUtils.boolToBytes(entry.getKey().getBoolean(), bytes, offset);
-          offset += 1;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      case INT32:
-      case DATE:
-        bytes = new byte[offset + Integer.BYTES + (Integer.BYTES + Long.BYTES) * countMap.size()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += Integer.BYTES;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          BytesUtils.intToBytes(entry.getKey().getInt(), bytes, offset);
-          offset += Integer.BYTES;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      case FLOAT:
-        bytes = new byte[offset + Integer.BYTES + (Float.BYTES + Long.BYTES) * countMap.size()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += Integer.BYTES;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          BytesUtils.floatToBytes(entry.getKey().getFloat(), bytes, offset);
-          offset += Float.BYTES;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        bytes = new byte[offset + Integer.BYTES + (Long.BYTES + Long.BYTES) * countMap.size()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += Integer.BYTES;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          BytesUtils.longToBytes(entry.getKey().getLong(), bytes, offset);
-          offset += Long.BYTES;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      case DOUBLE:
-        bytes = new byte[offset + Integer.BYTES + (Double.BYTES + Long.BYTES) * countMap.size()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += Integer.BYTES;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          BytesUtils.doubleToBytes(entry.getKey().getDouble(), bytes, offset);
-          offset += Double.BYTES;
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        bytes =
-            new byte
-                [offset
-                    + Integer.BYTES
-                    + (Integer.BYTES + Long.BYTES) * countMap.size()
-                    + countMap.keySet().stream()
-                        .mapToInt(key -> key.getBinary().getValues().length)
-                        .sum()];
-        BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
-        if (nullCounts.get(groupId) != 0) {
-          BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
-        }
-        BytesUtils.intToBytes(countMap.size(), bytes, offset);
-        offset += Integer.BYTES;
-        for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
-          Binary binary = entry.getKey().getBinary();
-          serializeBinaryValue(binary, bytes, offset);
-          offset += (Integer.BYTES + binary.getLength());
-          BytesUtils.longToBytes(entry.getValue(), bytes, offset);
-          offset += Long.BYTES;
-        }
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    int valueSize = countMap.keySet().stream().mapToInt(type::calcTypeSize).sum();
+    byte[] bytes = new byte[offset + Integer.BYTES + valueSize + Long.BYTES * countMap.size()];
+    BytesUtils.boolToBytes(nullCounts.get(groupId) != 0, bytes, 0);
+    if (nullCounts.get(groupId) != 0) {
+      BytesUtils.longToBytes(nullCounts.get(groupId), bytes, 1);
+    }
+    BytesUtils.intToBytes(countMap.size(), bytes, offset);
+    offset += Integer.BYTES;
+    for (Map.Entry<TsPrimitiveType, Long> entry : countMap.entrySet()) {
+      TsPrimitiveType key = entry.getKey();
+      int keySize = type.calcTypeSize(key);
+      type.toBytes(key, bytes, offset);
+      offset += keySize;
+      BytesUtils.longToBytes(entry.getValue(), bytes, offset);
+      offset += Long.BYTES;
     }
-
     return bytes;
   }
 
@@ -314,76 +192,11 @@ public class GroupedModeAccumulator implements GroupedAccumulator {
     offset += Integer.BYTES;
 
     HashMap<TsPrimitiveType, Long> countMap = countMaps.get(groupId);
-
-    switch (seriesDataType) {
-      case BOOLEAN:
-        for (int i = 0; i < size; i++) {
-          TsPrimitiveType key = new TsPrimitiveType.TsBoolean(bytesToBool(bytes, offset));
-          offset += 1;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case INT32:
-      case DATE:
-        for (int i = 0; i < size; i++) {
-          TsPrimitiveType key = new TsPrimitiveType.TsInt(BytesUtils.bytesToInt(bytes, offset));
-          offset += Integer.BYTES;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < size; i++) {
-          TsPrimitiveType key = new TsPrimitiveType.TsFloat(BytesUtils.bytesToFloat(bytes, offset));
-          offset += Float.BYTES;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        for (int i = 0; i < size; i++) {
-          TsPrimitiveType key =
-              new TsPrimitiveType.TsLong(
-                  BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset));
-          offset += Long.BYTES;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case DOUBLE:
-        for (int i = 0; i < size; i++) {
-          TsPrimitiveType key =
-              new TsPrimitiveType.TsDouble(BytesUtils.bytesToDouble(bytes, offset));
-          offset += Double.BYTES;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        for (int i = 0; i < size; i++) {
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          TsPrimitiveType key =
-              new TsPrimitiveType.TsBinary(new Binary(BytesUtils.subBytes(bytes, offset, length)));
-          offset += length;
-          long count = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          offset += Long.BYTES;
-          countMap.compute(key, (k, v) -> v == null ? count : v + count);
-        }
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(UNSUPPORTED_TYPE_MESSAGE, seriesDataType));
+    ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, bytes.length - offset);
+    for (int i = 0; i < size; i++) {
+      TsPrimitiveType key = type.deserialize(buffer);
+      long count = buffer.getLong();
+      countMap.compute(key, (k, v) -> v == null ? count : v + count);
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedPercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedPercentileAccumulator.java
index 79b9017d1d2..149e42b1296 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedPercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedPercentileAccumulator.java
@@ -22,12 +22,15 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.g
 import org.apache.iotdb.calc.execution.operator.source.relational.Percentile;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.PercentileBigArray;
+import org.apache.iotdb.calc.i18n.CalcMessages;
 import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.exception.SemanticException;
 
 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.type.Type;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -39,6 +42,8 @@ public class GroupedPercentileAccumulator implements GroupedAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedPercentileAccumulator.class);
   private final TSDataType seriesDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
+  private final TypeServices.NumericResultWriter resultWriter;
   // percentage is a query-level constant; it is read once from the first input/intermediate and
   // kept fixed afterwards, so it never gets reset to 0 by a later all-null batch.
   private double percentage;
@@ -51,6 +56,17 @@ public class GroupedPercentileAccumulator implements GroupedAccumulator {
       TSDataType seriesDataType, MemoryReservationManager memoryReservationManager) {
     this.seriesDataType = seriesDataType;
     this.memoryReservationManager = memoryReservationManager;
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueConverter =
+        TypeServices.PERCENTILE_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_PERCENTILE_AGGREGATION,
+                            seriesDataType)));
+    this.resultWriter = TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(type);
     updateMemoryReservation();
   }
 
@@ -68,31 +84,14 @@ public class GroupedPercentileAccumulator implements GroupedAccumulator {
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
     if (arguments.length != 2) {
       throw new SemanticException(
-          String.format("PERCENTILE requires 2 arguments, but got %d", arguments.length));
+          String.format(CalcMessages.PERCENTILE_REQUIRES_TWO_ARGUMENTS, arguments.length));
     }
     if (!percentageInitialized) {
       percentage = arguments[1].getDouble(0);
       percentageInitialized = true;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(groupIds, arguments, mask);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments, mask);
-        break;
-      case FLOAT:
-        addFloatInput(groupIds, arguments, mask);
-        break;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments, mask);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in PERCENTILE Aggregation: %s", seriesDataType));
-    }
+    addInput(groupIds, arguments[0], mask);
     updateMemoryReservation();
   }
 
@@ -136,23 +135,25 @@ public class GroupedPercentileAccumulator implements GroupedAccumulator {
       columnBuilder.appendNull();
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-        columnBuilder.writeInt((int) result);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong((long) result);
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat((float) result);
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(result);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in PERCENTILE Aggregation: %s", seriesDataType));
+    resultWriter.write(columnBuilder, result);
+  }
+
+  private void addInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int i = 0; i < positionCount; i++) {
+        if (!valueColumn.isNull(i)) {
+          array.get(groupIds[i]).addValue(valueConverter.convert(valueColumn, i));
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!valueColumn.isNull(position)) {
+          array.get(groupIds[position]).addValue(valueConverter.convert(valueColumn, position));
+        }
+      }
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java
index 5c7673d7c75..47b4373d01c 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java
@@ -21,10 +21,13 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.g
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BooleanBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.DoubleBigArray;
+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.read.common.type.Type;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
@@ -34,11 +37,22 @@ public class GroupedSumAccumulator implements GroupedAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedSumAccumulator.class);
   private final TSDataType argumentDataType;
+  private final TypeServices.ColumnToDoubleConverter valueConverter;
   private final BooleanBigArray initResult = new BooleanBigArray();
   private final DoubleBigArray sumValues = new DoubleBigArray();
 
   public GroupedSumAccumulator(TSDataType argumentDataType) {
     this.argumentDataType = argumentDataType;
+    Type type = Type.fromTsDataType(argumentDataType);
+    this.valueConverter =
+        TypeServices.AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE
+            .call(type)
+            .create(
+                () ->
+                    new UnSupportedDataTypeException(
+                        String.format(
+                            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_SUM_AGGREGATION,
+                            argumentDataType)));
   }
 
   @Override
@@ -55,30 +69,7 @@ public class GroupedSumAccumulator implements GroupedAccumulator {
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
     checkArgument(arguments.length == 1, "argument of SUM should be one column");
-    switch (argumentDataType) {
-      case INT32:
-        addIntInput(groupIds, arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(groupIds, arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format("Unsupported data type in SUM Aggregation: %s", argumentDataType));
-    }
+    addInput(groupIds, arguments[0], mask);
   }
 
   @Override
@@ -101,83 +92,14 @@ public class GroupedSumAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addIntInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult.set(groupIds[i], true);
-          sumValues.add(groupIds[i], column.getInt(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult.set(groupIds[position], true);
-          sumValues.add(groupIds[position], column.getInt(position));
-        }
-      }
-    }
-  }
-
-  private void addLongInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult.set(groupIds[i], true);
-          sumValues.add(groupIds[i], column.getLong(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult.set(groupIds[position], true);
-          sumValues.add(groupIds[position], column.getLong(position));
-        }
-      }
-    }
-  }
-
-  private void addFloatInput(int[] groupIds, Column column, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!column.isNull(i)) {
-          initResult.set(groupIds[i], true);
-          sumValues.add(groupIds[i], column.getFloat(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!column.isNull(position)) {
-          initResult.set(groupIds[position], true);
-          sumValues.add(groupIds[position], column.getFloat(position));
-        }
-      }
-    }
-  }
-
-  private void addDoubleInput(int[] groupIds, Column column, AggregationMask mask) {
+  private void addInput(int[] groupIds, Column column, AggregationMask mask) {
     int positionCount = mask.getSelectedPositionCount();
 
     if (mask.isSelectAll()) {
       for (int i = 0; i < positionCount; i++) {
         if (!column.isNull(i)) {
           initResult.set(groupIds[i], true);
-          sumValues.add(groupIds[i], column.getDouble(i));
+          sumValues.add(groupIds[i], valueConverter.convert(column, i));
         }
       }
     } else {
@@ -187,7 +109,7 @@ public class GroupedSumAccumulator implements GroupedAccumulator {
         position = selectedPositions[i];
         if (!column.isNull(position)) {
           initResult.set(groupIds[position], true);
-          sumValues.add(groupIds[position], column.getDouble(position));
+          sumValues.add(groupIds[position], valueConverter.convert(column, position));
         }
       }
     }
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 19583424db4..63c5eaaa249 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
@@ -60,6 +60,8 @@ import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.De
 import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator;
 import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition;
 import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedMaxMinByBaseAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BinaryBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BooleanBigArray;
@@ -89,6 +91,7 @@ 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.BytesUtils;
 import org.apache.tsfile.utils.DateUtils;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 import org.apache.tsfile.utils.TsPrimitiveType;
@@ -97,6 +100,7 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 import java.util.Comparator;
 import java.util.List;
 import java.util.function.Function;
+import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
 import java.util.function.IntUnaryOperator;
 import java.util.function.Supplier;
@@ -347,11 +351,188 @@ public class TypeServices {
   public static final TypeService<ColumnRowFunction> UPDATE_LAST_ROW_SERVICE =
       type ->
           (column, rowIndex) -> {
-            ColumnBuilder columnBuilder = type.createColumnBuilder(1);
+            // Use the status overload so specialized types such as DATE retain their data type.
+            ColumnBuilder columnBuilder = type.createColumnBuilder(null, 1);
             type.write(columnBuilder, column, rowIndex);
             return columnBuilder.build();
           };
 
+  /** Copies a column value into a reusable primitive result without boxing. */
+  public static final TypeService<PrimitiveColumnValueSetter>
+      PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    (target, column, position) -> target.setInt(type.getInt(column, position));
+                case INT64, TIMESTAMP ->
+                    (target, column, position) -> target.setLong(type.getLong(column, position));
+                case FLOAT ->
+                    (target, column, position) -> target.setFloat(type.getFloat(column, position));
+                case DOUBLE ->
+                    (target, column, position) ->
+                        target.setDouble(type.getDouble(column, position));
+                case BOOLEAN ->
+                    (target, column, position) ->
+                        target.setBoolean(type.getBoolean(column, position));
+                case TEXT, STRING, BLOB, OBJECT ->
+                    (target, column, position) ->
+                        target.setBinary(type.getBinary(column, position));
+                case ROW, UNKNOWN, VECTOR ->
+                    (target, column, position) -> {
+                      throw new UnSupportedDataTypeException(
+                              CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum())
+                          .setChecked(true);
+                    };
+              };
+
+  /** Selects the y-column reader used by MAX_BY and MIN_BY input processing. */
+  public static final TypeService<MaxMinByInput> MAX_MIN_BY_INPUT_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateInt(
+                                arguments[1].getInt(position), arguments[0], position));
+            case INT64, TIMESTAMP ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateLong(
+                                arguments[1].getLong(position), arguments[0], position));
+            case FLOAT ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateFloat(
+                                arguments[1].getFloat(position), arguments[0], position));
+            case DOUBLE ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateDouble(
+                                arguments[1].getDouble(position), arguments[0], position));
+            case TEXT, STRING, BLOB, OBJECT ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateBinary(
+                                arguments[1].getBinary(position), arguments[0], position));
+            case BOOLEAN ->
+                (arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateBoolean(
+                                arguments[1].getBoolean(position), arguments[0], position));
+            case ROW, UNKNOWN, VECTOR ->
+                (arguments, mask, updater) -> {
+                  throw unsupportedMaxMinByDataType(type);
+                };
+          };
+
+  /** Selects the y-value decoder used by MAX_BY and MIN_BY intermediate input processing. */
+  public static final TypeService<MaxMinByIntermediateInput> MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  int value = BytesUtils.bytesToInt(bytes, 0);
+                  readMaxMinByXFromBytes(bytes, Integer.BYTES, xType, columnBuilder);
+                  updater.updateInt(value, columnBuilder.build(), 0);
+                };
+            case INT64, TIMESTAMP ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  long value = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 0);
+                  readMaxMinByXFromBytes(bytes, Long.BYTES, xType, columnBuilder);
+                  updater.updateLong(value, columnBuilder.build(), 0);
+                };
+            case FLOAT ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  float value = BytesUtils.bytesToFloat(bytes, 0);
+                  readMaxMinByXFromBytes(bytes, Float.BYTES, xType, columnBuilder);
+                  updater.updateFloat(value, columnBuilder.build(), 0);
+                };
+            case DOUBLE ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  double value = BytesUtils.bytesToDouble(bytes, 0);
+                  readMaxMinByXFromBytes(bytes, Double.BYTES, xType, columnBuilder);
+                  updater.updateDouble(value, columnBuilder.build(), 0);
+                };
+            case TEXT, STRING, BLOB, OBJECT ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  int length = BytesUtils.bytesToInt(bytes, 0);
+                  int xOffset = Integer.BYTES + length;
+                  Binary value = new Binary(BytesUtils.subBytes(bytes, Integer.BYTES, length));
+                  readMaxMinByXFromBytes(bytes, xOffset, xType, columnBuilder);
+                  updater.updateBinary(value, columnBuilder.build(), 0);
+                };
+            case BOOLEAN ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  boolean value = BytesUtils.bytesToBool(bytes, 0);
+                  readMaxMinByXFromBytes(bytes, 1, xType, columnBuilder);
+                  updater.updateBoolean(value, columnBuilder.build(), 0);
+                };
+            case ROW, UNKNOWN, VECTOR ->
+                (bytes, xType, columnBuilder, updater) -> {
+                  throw unsupportedMaxMinByDataType(type);
+                };
+          };
+
+  /** Selects the type-specific decoder for a LAST intermediate value. */
+  public static final TypeService<LastValueDeserializer> LAST_VALUE_DESERIALIZER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  int value = BytesUtils.bytesToInt(bytes, offset);
+                  updater.updateInt(value, time, isOrderTimeNull);
+                };
+            case INT64, TIMESTAMP ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  long value = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
+                  updater.updateLong(value, time, isOrderTimeNull);
+                };
+            case FLOAT ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  float value = BytesUtils.bytesToFloat(bytes, offset);
+                  updater.updateFloat(value, time, isOrderTimeNull);
+                };
+            case DOUBLE ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  double value = BytesUtils.bytesToDouble(bytes, offset);
+                  updater.updateDouble(value, time, isOrderTimeNull);
+                };
+            case TEXT, BLOB, OBJECT, STRING ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  int length = BytesUtils.bytesToInt(bytes, offset);
+                  Binary value =
+                      new Binary(BytesUtils.subBytes(bytes, offset + Integer.BYTES, length));
+                  updater.updateBinary(value, time, isOrderTimeNull);
+                };
+            case BOOLEAN ->
+                (bytes, offset, time, isOrderTimeNull, updater) ->
+                    updater.updateBoolean(
+                        BytesUtils.bytesToBool(bytes, offset), time, isOrderTimeNull);
+            case ROW, UNKNOWN, VECTOR ->
+                (bytes, offset, time, isOrderTimeNull, updater) -> {
+                  throw new UnSupportedDataTypeException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
   public static final TypeService<Function<TsPrimitiveType, Object>>
       PRIMITIVE_TYPE_VALUE_EXTRACTOR_SERVICE =
           type ->
@@ -373,6 +554,70 @@ public class TypeServices {
                     };
               };
 
+  /** Reads, serializes, deserializes, and writes Mode values without TsPrimitiveType conversion. */
+  public static final TypeService<ModeValueService> MODE_VALUE_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                modeValueService(
+                    type,
+                    type::getBoolean,
+                    BytesUtils::bytesToBool,
+                    (value, bytes, offset) -> {
+                      BytesUtils.boolToBytes((boolean) value, bytes, offset);
+                      return Byte.BYTES;
+                    },
+                    (columnBuilder, value) -> type.writeBoolean(columnBuilder, (boolean) value));
+            case INT32, DATE ->
+                modeValueService(
+                    type,
+                    type::getInt,
+                    BytesUtils::bytesToInt,
+                    (value, bytes, offset) -> {
+                      BytesUtils.intToBytes((int) value, bytes, offset);
+                      return Integer.BYTES;
+                    },
+                    (columnBuilder, value) -> type.writeInt(columnBuilder, (int) value));
+            case INT64, TIMESTAMP ->
+                modeValueService(
+                    type,
+                    type::getLong,
+                    (bytes, offset) -> BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset),
+                    (value, bytes, offset) -> {
+                      BytesUtils.longToBytes((long) value, bytes, offset);
+                      return Long.BYTES;
+                    },
+                    (columnBuilder, value) -> type.writeLong(columnBuilder, (long) value));
+            case FLOAT ->
+                modeValueService(
+                    type,
+                    type::getFloat,
+                    BytesUtils::bytesToFloat,
+                    (value, bytes, offset) -> {
+                      BytesUtils.floatToBytes((float) value, bytes, offset);
+                      return Float.BYTES;
+                    },
+                    (columnBuilder, value) -> type.writeFloat(columnBuilder, (float) value));
+            case DOUBLE ->
+                modeValueService(
+                    type,
+                    type::getDouble,
+                    BytesUtils::bytesToDouble,
+                    (value, bytes, offset) -> {
+                      BytesUtils.doubleToBytes((double) value, bytes, offset);
+                      return Double.BYTES;
+                    },
+                    (columnBuilder, value) -> type.writeDouble(columnBuilder, (double) value));
+            case TEXT, STRING, BLOB ->
+                modeValueService(
+                    type,
+                    type::getBinary,
+                    TypeServices::deserializeModeBinary,
+                    TypeServices::serializeModeBinary,
+                    (columnBuilder, value) -> type.writeBinary(columnBuilder, (Binary) value));
+            case OBJECT, ROW, UNKNOWN, VECTOR -> unsupportedModeValueService(type);
+          };
+
   // 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 =
@@ -386,6 +631,255 @@ public class TypeServices {
                         };
               };
 
+  /** Numeric inputs supported by SUM, AVG and percentile aggregations. */
+  public static final TypeService<ColumnToDoubleConverterFactory>
+      AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, INT64, FLOAT, DOUBLE -> ignored -> type::getDouble;
+                case BOOLEAN, DATE, TIMESTAMP, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    exceptionSupplier ->
+                        (column, position) -> {
+                          throw exceptionSupplier.get();
+                        };
+              };
+
+  /** Numeric inputs supported by percentile aggregations, including TIMESTAMP. */
+  public static final TypeService<ColumnToDoubleConverterFactory>
+      PERCENTILE_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, INT64, TIMESTAMP, FLOAT, DOUBLE -> ignored -> type::getDouble;
+                case BOOLEAN, DATE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    exceptionSupplier ->
+                        (column, position) -> {
+                          throw exceptionSupplier.get();
+                        };
+              };
+
+  /** Writes a numeric percentile result in the input column's native type. */
+  public static final TypeService<NumericResultWriter> NUMERIC_RESULT_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (builder, value) -> type.writeInt(builder, (int) value);
+            case INT64, TIMESTAMP -> (builder, value) -> type.writeLong(builder, (long) value);
+            case FLOAT -> (builder, value) -> type.writeFloat(builder, (float) value);
+            case DOUBLE -> (builder, value) -> type.writeDouble(builder, value);
+            case BOOLEAN, DATE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (builder, value) -> {
+                  throw new UnSupportedDataTypeException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
+  /** Updates a reusable primitive result with a column value when it is more extreme. */
+  public static final TypeService<ColumnValueUpdater> MIN_COLUMN_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (result, column, position, initialized) -> {
+                  int value = type.getInt(column, position);
+                  if (!initialized || value < result.getInt()) {
+                    result.setInt(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64, TIMESTAMP ->
+                (result, column, position, initialized) -> {
+                  long value = type.getLong(column, position);
+                  if (!initialized || value < result.getLong()) {
+                    result.setLong(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, column, position, initialized) -> {
+                  float value = type.getFloat(column, position);
+                  if (!initialized || value < result.getFloat()) {
+                    result.setFloat(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, column, position, initialized) -> {
+                  double value = type.getDouble(column, position);
+                  if (!initialized || value < result.getDouble()) {
+                    result.setDouble(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN ->
+                (result, column, position, initialized) -> {
+                  boolean value = type.getBoolean(column, position);
+                  if (!initialized || !value) {
+                    result.setBoolean(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case TEXT, STRING, BLOB ->
+                (result, column, position, initialized) -> {
+                  Binary value = type.getBinary(column, position);
+                  if (!initialized || value.compareTo(result.getBinary()) < 0) {
+                    result.setBinary(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, column, position, initialized) -> {
+                  throw new UnSupportedDataTypeException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
+  /** Updates a reusable primitive result with a column value when it is more extreme. */
+  public static final TypeService<ColumnValueUpdater> MAX_COLUMN_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (result, column, position, initialized) -> {
+                  int value = type.getInt(column, position);
+                  if (!initialized || value > result.getInt()) {
+                    result.setInt(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64, TIMESTAMP ->
+                (result, column, position, initialized) -> {
+                  long value = type.getLong(column, position);
+                  if (!initialized || value > result.getLong()) {
+                    result.setLong(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, column, position, initialized) -> {
+                  float value = type.getFloat(column, position);
+                  if (!initialized || value > result.getFloat()) {
+                    result.setFloat(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, column, position, initialized) -> {
+                  double value = type.getDouble(column, position);
+                  if (!initialized || value > result.getDouble()) {
+                    result.setDouble(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN ->
+                (result, column, position, initialized) -> {
+                  boolean value = type.getBoolean(column, position);
+                  if (!initialized || value) {
+                    result.setBoolean(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case TEXT, STRING, BLOB ->
+                (result, column, position, initialized) -> {
+                  Binary value = type.getBinary(column, position);
+                  if (!initialized || value.compareTo(result.getBinary()) > 0) {
+                    result.setBinary(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, column, position, initialized) -> {
+                  throw new UnSupportedDataTypeException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
+  /** Updates a reusable primitive result with the value having the greatest absolute magnitude. */
+  public static final TypeService<ColumnValueUpdater> EXTREME_COLUMN_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (result, column, position, initialized) -> {
+                  int value = type.getInt(column, position);
+                  int candidate = result.getInt();
+                  if (!initialized || compareExtreme(value, candidate) > 0) {
+                    result.setInt(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64 ->
+                (result, column, position, initialized) -> {
+                  long value = type.getLong(column, position);
+                  long candidate = result.getLong();
+                  if (!initialized || compareExtreme(value, candidate) > 0) {
+                    result.setLong(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, column, position, initialized) -> {
+                  float value = type.getFloat(column, position);
+                  float candidate = result.getFloat();
+                  float absValue = Math.abs(value);
+                  float absCandidate = Math.abs(candidate);
+                  if (!initialized
+                      || absValue > absCandidate
+                      || absValue == absCandidate && value > candidate) {
+                    result.setFloat(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, column, position, initialized) -> {
+                  double value = type.getDouble(column, position);
+                  double candidate = result.getDouble();
+                  double absValue = Math.abs(value);
+                  double absCandidate = Math.abs(candidate);
+                  if (!initialized
+                      || absValue > absCandidate
+                      || absValue == absCandidate && value > candidate) {
+                    result.setDouble(value);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, column, position, initialized) -> {
+                  throw new UnSupportedDataTypeException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
+  /** Adds a column value to an HLL using the column's native primitive accessor. */
+  public static final TypeService<HyperLogLogColumnAdder> HYPER_LOG_LOG_COLUMN_ADDER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> (hll, column, position) -> hll.add(column.getBoolean(position));
+            case INT32, DATE -> (hll, column, position) -> hll.add(column.getInt(position));
+            case INT64, TIMESTAMP -> (hll, column, position) -> hll.add(column.getLong(position));
+            case FLOAT -> (hll, column, position) -> hll.add(column.getFloat(position));
+            case DOUBLE -> (hll, column, position) -> hll.add(column.getDouble(position));
+            case TEXT, BLOB, STRING, OBJECT ->
+                (hll, column, position) -> hll.add(column.getBinary(position));
+            case ROW, UNKNOWN, VECTOR ->
+                (hll, column, position) -> {
+                  throw new UnSupportedDataTypeException(
+                          CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum())
+                      .setChecked(true);
+                };
+          };
+
   // RANGE frame offsets must retain each primitive type's native overflow and precision rules.
   public static final TypeService<RangeFrameComparator> RANGE_FRAME_COMPARATOR_SERVICE =
       type ->
@@ -593,11 +1087,23 @@ public class TypeServices {
     GREATEST_COLUMN_TRANSFORMER_SERVICE.check();
     LEAST_COLUMN_TRANSFORMER_SERVICE.check();
     UPDATE_LAST_ROW_SERVICE.check();
+    PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.check();
+    MAX_MIN_BY_INPUT_SERVICE.check();
+    MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE.check();
+    LAST_VALUE_DESERIALIZER_SERVICE.check();
     MERGE_SORT_COMPARATOR_SERVICE.check();
     MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE.check();
     MEMORY_USAGE_OF_ONE_SERIALIZABLE_ROW_FIELD_SERVICE.check();
     PRIMITIVE_TYPE_VALUE_EXTRACTOR_SERVICE.check();
+    MODE_VALUE_SERVICE.check();
     NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE.check();
+    AGGREGATION_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE.check();
+    PERCENTILE_NUMERIC_COLUMN_TO_DOUBLE_CONVERTER_SERVICE.check();
+    NUMERIC_RESULT_WRITER_SERVICE.check();
+    MIN_COLUMN_VALUE_UPDATER_SERVICE.check();
+    MAX_COLUMN_VALUE_UPDATER_SERVICE.check();
+    EXTREME_COLUMN_VALUE_UPDATER_SERVICE.check();
+    HYPER_LOG_LOG_COLUMN_ADDER_SERVICE.check();
     RANGE_FRAME_COMPARATOR_SERVICE.check();
     DEFAULT_ENCODING_BY_TYPE_SERVICE.check();
     DEFAULT_VALUE_WRITER_SERVICE.check();
@@ -618,6 +1124,141 @@ public class TypeServices {
         CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
   }
 
+  private static UnSupportedDataTypeException unsupportedMaxMinByDataType(final Type type) {
+    return new UnSupportedDataTypeException(
+        String.format(
+            CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, type.getTypeEnum()));
+  }
+
+  private static ModeValueService modeValueService(
+      Type type,
+      ModeValueGetter getter,
+      ModeValueDeserializer deserializer,
+      ModeValueSerializer serializer,
+      ModeValueWriter writer) {
+    return new ModeValueService() {
+      @Override
+      public Object getValue(Column column, int position) {
+        return getter.get(column, position);
+      }
+
+      @Override
+      public Object deserialize(byte[] bytes, int offset) {
+        return deserializer.deserialize(bytes, offset);
+      }
+
+      @Override
+      public int calcTypeSize(Object value) {
+        return type.calcTypeSize(value);
+      }
+
+      @Override
+      public int serialize(Object value, byte[] bytes, int offset) {
+        return serializer.serialize(value, bytes, offset);
+      }
+
+      @Override
+      public void write(ColumnBuilder columnBuilder, Object value) {
+        writer.write(columnBuilder, value);
+      }
+    };
+  }
+
+  private static ModeValueService unsupportedModeValueService(final Type type) {
+    return new ModeValueService() {
+      @Override
+      public Object getValue(Column column, int position) {
+        throw unsupportedModeValueDataType(type);
+      }
+
+      @Override
+      public Object deserialize(byte[] bytes, int offset) {
+        throw unsupportedModeValueDataType(type);
+      }
+
+      @Override
+      public int calcTypeSize(Object value) {
+        throw unsupportedModeValueDataType(type);
+      }
+
+      @Override
+      public int serialize(Object value, byte[] bytes, int offset) {
+        throw unsupportedModeValueDataType(type);
+      }
+
+      @Override
+      public void write(ColumnBuilder columnBuilder, Object value) {
+        throw unsupportedModeValueDataType(type);
+      }
+    };
+  }
+
+  private static UnSupportedDataTypeException unsupportedModeValueDataType(final Type type) {
+    return new UnSupportedDataTypeException(CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum())
+        .setChecked(true);
+  }
+
+  private static int serializeModeBinary(Object value, byte[] bytes, int offset) {
+    Binary binary = (Binary) value;
+    BytesUtils.intToBytes(binary.getLength(), bytes, offset);
+    System.arraycopy(binary.getValues(), 0, bytes, offset + Integer.BYTES, binary.getLength());
+    return Integer.BYTES + binary.getLength();
+  }
+
+  private static Binary deserializeModeBinary(byte[] bytes, int offset) {
+    int length = BytesUtils.bytesToInt(bytes, offset);
+    return new Binary(BytesUtils.subBytes(bytes, offset + Integer.BYTES, length));
+  }
+
+  private static void forEachSelectedPosition(
+      Column column, AggregationMask mask, IntConsumer positionConsumer) {
+    int positionCount = mask.getSelectedPositionCount();
+    if (mask.isSelectAll()) {
+      for (int position = 0; position < positionCount; position++) {
+        if (!column.isNull(position)) {
+          positionConsumer.accept(position);
+        }
+      }
+    } else {
+      int[] selectedPositions = mask.getSelectedPositions();
+      for (int i = 0; i < positionCount; i++) {
+        int position = selectedPositions[i];
+        if (!column.isNull(position)) {
+          positionConsumer.accept(position);
+        }
+      }
+    }
+  }
+
+  private static void readMaxMinByXFromBytes(
+      byte[] bytes, int offset, Type xType, ColumnBuilder columnBuilder) {
+    if (BytesUtils.bytesToBool(bytes, offset)) {
+      columnBuilder.appendNull();
+    } else {
+      xType.write(columnBuilder, bytes, offset + 1);
+    }
+  }
+
+  private static int compareExtreme(int left, int right) {
+    int absComparison = Long.compare(Math.abs((long) left), Math.abs((long) right));
+    return absComparison == 0 ? Integer.compare(left, right) : absComparison;
+  }
+
+  private static int compareExtreme(long left, long right) {
+    int absComparison = compareAbs(left, right);
+    return absComparison == 0 ? Long.compare(left, right) : absComparison;
+  }
+
+  private static int compareAbs(long left, long right) {
+    if (left == Long.MIN_VALUE) {
+      return right == Long.MIN_VALUE ? 0 : 1;
+    }
+    if (right == Long.MIN_VALUE) {
+      return -1;
+    }
+    return Long.compare(Math.abs(left), Math.abs(right));
+  }
+
   public interface DefaultEncodingProvider {
     TSEncoding getDefaultBooleanEncoding();
 
@@ -637,6 +1278,88 @@ public class TypeServices {
     Column apply(Column column, int rowIndex);
   }
 
+  @FunctionalInterface
+  public interface PrimitiveColumnValueSetter {
+    void set(TsPrimitiveType target, Column column, int position);
+  }
+
+  public interface ModeValueService {
+    Object getValue(Column column, int position);
+
+    Object deserialize(byte[] bytes, int offset);
+
+    int calcTypeSize(Object value);
+
+    int serialize(Object value, byte[] bytes, int offset);
+
+    void write(ColumnBuilder columnBuilder, Object value);
+  }
+
+  @FunctionalInterface
+  private interface ModeValueGetter {
+    Object get(Column column, int position);
+  }
+
+  @FunctionalInterface
+  private interface ModeValueDeserializer {
+    Object deserialize(byte[] bytes, int offset);
+  }
+
+  @FunctionalInterface
+  private interface ModeValueSerializer {
+    int serialize(Object value, byte[] bytes, int offset);
+  }
+
+  @FunctionalInterface
+  private interface ModeValueWriter {
+    void write(ColumnBuilder columnBuilder, Object value);
+  }
+
+  public interface MaxMinByValueUpdater {
+    void updateInt(int value, Column xColumn, int xIndex);
+
+    void updateLong(long value, Column xColumn, int xIndex);
+
+    void updateFloat(float value, Column xColumn, int xIndex);
+
+    void updateDouble(double value, Column xColumn, int xIndex);
+
+    void updateBinary(Binary value, Column xColumn, int xIndex);
+
+    void updateBoolean(boolean value, Column xColumn, int xIndex);
+  }
+
+  @FunctionalInterface
+  public interface MaxMinByInput {
+    void add(Column[] arguments, AggregationMask mask, MaxMinByValueUpdater updater);
+  }
+
+  @FunctionalInterface
+  public interface MaxMinByIntermediateInput {
+    void update(
+        byte[] bytes, Type xType, ColumnBuilder columnBuilder, MaxMinByValueUpdater updater);
+  }
+
+  @FunctionalInterface
+  public interface LastValueDeserializer {
+    void deserialize(
+        byte[] bytes, int offset, long time, boolean isOrderTimeNull, LastValueUpdater updater);
+  }
+
+  public interface LastValueUpdater {
+    void updateInt(int value, long time, boolean isOrderTimeNull);
+
+    void updateLong(long value, long time, boolean isOrderTimeNull);
+
+    void updateFloat(float value, long time, boolean isOrderTimeNull);
+
+    void updateDouble(double value, long time, boolean isOrderTimeNull);
+
+    void updateBinary(Binary value, long time, boolean isOrderTimeNull);
+
+    void updateBoolean(boolean value, long time, boolean isOrderTimeNull);
+  }
+
   @FunctionalInterface
   public interface ColumnToDoubleConverter {
     double convert(Column column, int position);
@@ -653,6 +1376,21 @@ public class TypeServices {
     ColumnToDoubleConverter create(Supplier<? extends RuntimeException> exceptionSupplier);
   }
 
+  @FunctionalInterface
+  public interface NumericResultWriter {
+    void write(ColumnBuilder builder, double value);
+  }
+
+  @FunctionalInterface
+  public interface ColumnValueUpdater {
+    boolean update(TsPrimitiveType result, Column column, int position, boolean initialized);
+  }
+
+  @FunctionalInterface
+  public interface HyperLogLogColumnAdder {
+    void add(HyperLogLog hll, Column column, int position);
+  }
+
   @FunctionalInterface
   public interface RangeFrameComparator {
     boolean compare(
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
index 33d3723550c..4d34c865f10 100644
--- 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
@@ -26,14 +26,18 @@ import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 
 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.TsBlock;
+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.TimeColumn;
 import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.utils.Binary;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -171,6 +175,30 @@ public class TypeServicesTest {
                 .apply(input, 0));
   }
 
+  @Test
+  public void testModeValueServiceUsesNativeValues() {
+    assertModeValueRoundTrip(
+        TSDataType.BOOLEAN, new BooleanColumn(1, Optional.empty(), new boolean[] {true}), true);
+    assertModeValueRoundTrip(
+        TSDataType.INT32, new IntColumn(1, Optional.empty(), new int[] {123}), 123);
+    assertModeValueRoundTrip(
+        TSDataType.DATE, new IntColumn(1, Optional.empty(), new int[] {456}, TSDataType.DATE), 456);
+    assertModeValueRoundTrip(
+        TSDataType.INT64, new LongColumn(1, Optional.empty(), new long[] {789L}), 789L);
+    assertModeValueRoundTrip(
+        TSDataType.TIMESTAMP, new LongColumn(1, Optional.empty(), new long[] {987L}), 987L);
+    assertModeValueRoundTrip(
+        TSDataType.FLOAT, new FloatColumn(1, Optional.empty(), new float[] {1.25F}), 1.25F);
+    assertModeValueRoundTrip(
+        TSDataType.DOUBLE, new DoubleColumn(1, Optional.empty(), new double[] {2.5}), 2.5);
+
+    Binary binary = new Binary(new byte[] {1, 2, 3});
+    Column binaryColumn = new BinaryColumn(1, Optional.empty(), new Binary[] {binary});
+    assertModeValueRoundTrip(TSDataType.TEXT, binaryColumn, binary);
+    assertModeValueRoundTrip(TSDataType.STRING, binaryColumn, binary);
+    assertModeValueRoundTrip(TSDataType.BLOB, binaryColumn, binary);
+  }
+
   // Covers every supported RANGE-frame type and guards native integer overflow and long precision.
   @Test
   public void testRangeFrameComparatorPreservesNativeArithmetic() {
@@ -226,6 +254,23 @@ public class TypeServicesTest {
         .convert(column, 0);
   }
 
+  private static void assertModeValueRoundTrip(
+      final TSDataType dataType, final Column input, final Object expectedValue) {
+    Type type = Type.fromTsDataType(dataType);
+    TypeServices.ModeValueService valueService = TypeServices.MODE_VALUE_SERVICE.call(type);
+
+    Object value = valueService.getValue(input, 0);
+    assertEquals(expectedValue, value);
+    byte[] bytes = new byte[valueService.calcTypeSize(value)];
+    assertEquals(bytes.length, valueService.serialize(value, bytes, 0));
+    Object deserializedValue = valueService.deserialize(bytes, 0);
+    assertEquals(value, deserializedValue);
+
+    ColumnBuilder outputBuilder = type.createColumnBuilder(null, 1);
+    valueService.write(outputBuilder, deserializedValue);
+    assertTrue(input.arePositionsEqual(0, outputBuilder.build(), 0));
+  }
+
   private static void assertRangeFrameComparator(
       final TSDataType dataType, final Column valueColumn, final Column offsetColumn) {
     assertTrue(
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
index 93c2db32e5b..c0304b267de 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
@@ -463,54 +463,7 @@ public class AggregateProcessor implements PipeProcessor {
       synchronized (stateReference) {
         final TimeSeriesRuntimeState state = stateReference.get();
         try {
-          switch (row.getDataType(index)) {
-            case BOOLEAN:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getBoolean(index), outputMinReportIntervalMilliseconds);
-              break;
-            case INT32:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getInt(index), outputMinReportIntervalMilliseconds);
-              break;
-            case DATE:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getDate(index), outputMinReportIntervalMilliseconds);
-              break;
-            case INT64:
-            case TIMESTAMP:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getLong(index), outputMinReportIntervalMilliseconds);
-              break;
-            case FLOAT:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getFloat(index), outputMinReportIntervalMilliseconds);
-              break;
-            case DOUBLE:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getDouble(index), outputMinReportIntervalMilliseconds);
-              break;
-            case TEXT:
-            case STRING:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getString(index), outputMinReportIntervalMilliseconds);
-              break;
-            case BLOB:
-            case OBJECT:
-              result =
-                  state.updateWindows(
-                      timestamp, row.getBinary(index), outputMinReportIntervalMilliseconds);
-              break;
-            default:
-              throw new UnsupportedOperationException(
-                  String.format("The type %s is not supported", row.getDataType(index)));
-          }
+          result = state.updateWindows(timestamp, row, index, outputMinReportIntervalMilliseconds);
           if (Objects.nonNull(result)) {
             collectWindowOutputs(result.getLeft(), timeSeries, rowCollector);
             if (Objects.nonNull(result.getRight())) {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/TimeSeriesRuntimeState.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/TimeSeriesRuntimeState.java
index 2cb9a414690..cc8bfca7761 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/TimeSeriesRuntimeState.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/TimeSeriesRuntimeState.java
@@ -25,8 +25,12 @@ import org.apache.iotdb.db.pipe.processor.aggregate.window.datastructure.TimeSer
 import org.apache.iotdb.db.pipe.processor.aggregate.window.datastructure.WindowOutput;
 import org.apache.iotdb.db.pipe.processor.aggregate.window.datastructure.WindowState;
 import org.apache.iotdb.db.pipe.processor.aggregate.window.processor.AbstractWindowingProcessor;
+import org.apache.iotdb.db.utils.TypeServices;
+import org.apache.iotdb.pipe.api.access.Row;
 import org.apache.iotdb.pipe.api.type.Binary;
 
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.Pair;
 import org.apache.tsfile.utils.PublicBAOS;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -61,6 +65,7 @@ public class TimeSeriesRuntimeState {
 
   // Variables to avoid "new" operation
   private final List<WindowOutput> outputList = new ArrayList<>();
+  private TypeServices.Pipe.AggregateRowValueUpdater rowValueUpdater;
 
   public TimeSeriesRuntimeState(
       final Map<String, AggregatedResultOperator> aggregatorOutputName2OperatorMap,
@@ -74,6 +79,22 @@ public class TimeSeriesRuntimeState {
     this.windowingProcessor = windowingProcessor;
   }
 
+  public Pair<List<WindowOutput>, Pair<Long, ByteBuffer>> updateWindows(
+      final long timestamp,
+      final Row row,
+      final int columnIndex,
+      final long outputMinReportIntervalMilliseconds)
+      throws IOException {
+    if (rowValueUpdater == null) {
+      rowValueUpdater =
+          TypeServices.Pipe.AGGREGATE_ROW_VALUE_UPDATER_SERVICE.call(
+              Type.fromTsDataType(
+                  TSDataType.getTsDataType(row.getDataType(columnIndex).getType())));
+    }
+    return rowValueUpdater.update(
+        this, timestamp, row, columnIndex, outputMinReportIntervalMilliseconds);
+  }
+
   // The following "updateWindows" are the same except for the input value types
   public Pair<List<WindowOutput>, Pair<Long, ByteBuffer>> updateWindows(
       final long timestamp, final boolean value, final long outputMinReportIntervalMilliseconds)
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 295ebb30939..f234b757151 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
@@ -55,7 +55,9 @@ import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.i18n.DataNodeMiscMessages;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.i18n.DataNodeQueryMessages;
+import org.apache.iotdb.db.pipe.processor.aggregate.TimeSeriesRuntimeState;
 import org.apache.iotdb.db.pipe.processor.aggregate.operator.intermediateresult.sametype.numeric.AbstractSameTypeNumericOperator;
+import org.apache.iotdb.db.pipe.processor.aggregate.window.datastructure.WindowOutput;
 import org.apache.iotdb.db.queryengine.execution.operator.window.EqualBinaryWindowManager;
 import org.apache.iotdb.db.queryengine.execution.operator.window.EqualBooleanWindowManager;
 import org.apache.iotdb.db.queryengine.execution.operator.window.EqualDoubleWindowManager;
@@ -3259,6 +3261,17 @@ public class TypeServices {
       Object get(Row row, int columnIndex);
     }
 
+    @FunctionalInterface
+    public interface AggregateRowValueUpdater {
+      Pair<List<WindowOutput>, Pair<Long, ByteBuffer>> update(
+          TimeSeriesRuntimeState state,
+          long timestamp,
+          Row row,
+          int columnIndex,
+          long reportInterval)
+          throws IOException;
+    }
+
     @FunctionalInterface
     public interface AggregateTabletColumnValueWriter {
       void write(Object column, int rowIndex, Object value);
@@ -3338,6 +3351,43 @@ public class TypeServices {
                   };
             };
 
+    public static final TypeService<AggregateRowValueUpdater> AGGREGATE_ROW_VALUE_UPDATER_SERVICE =
+        type ->
+            switch (type.getTypeEnum()) {
+              case BOOLEAN ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getBoolean(columnIndex), reportInterval);
+              case INT32 ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getInt(columnIndex), reportInterval);
+              case DATE ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getDate(columnIndex), reportInterval);
+              case INT64, TIMESTAMP ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getLong(columnIndex), reportInterval);
+              case FLOAT ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getFloat(columnIndex), reportInterval);
+              case DOUBLE ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getDouble(columnIndex), reportInterval);
+              case TEXT, STRING ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getString(columnIndex), reportInterval);
+              case BLOB, OBJECT ->
+                  (state, timestamp, row, columnIndex, reportInterval) ->
+                      state.updateWindows(timestamp, row.getBinary(columnIndex), reportInterval);
+              case ROW, UNKNOWN, VECTOR ->
+                  (state, timestamp, row, columnIndex, reportInterval) -> {
+                    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 ->
@@ -3521,6 +3571,7 @@ public class TypeServices {
       // 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();
+      AGGREGATE_ROW_VALUE_UPDATER_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/library-udf/pom.xml b/library-udf/pom.xml
index a452bbcc70a..e228200a616 100644
--- a/library-udf/pom.xml
+++ b/library-udf/pom.xml
@@ -38,6 +38,11 @@
             <artifactId>common</artifactId>
             <version>${tsfile.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.tsfile</groupId>
+            <artifactId>tsfile</artifactId>
+            <version>${tsfile.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.iotdb</groupId>
             <artifactId>udf-api</artifactId>
diff --git a/library-udf/src/main/i18n/en/org/apache/iotdb/library/i18n/LibraryUdfMessages.java b/library-udf/src/main/i18n/en/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
index 9509779f1f3..6bdaa3ea359 100644
--- a/library-udf/src/main/i18n/en/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
+++ b/library-udf/src/main/i18n/en/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
@@ -60,6 +60,9 @@ public final class LibraryUdfMessages {
   public static final String ALL_VALUES_ARE_NAN = "All values are NaN";
 
   // UDFEnvelopeAnalysis
+  public static final String FREQUENCY_MUST_BE_POSITIVE = "The param 'frequency' must > 0.";
+  public static final String AMPLIFICATION_MUST_BE_AT_LEAST_1 =
+      "The param 'amplification' must >= 1.";
   public static final String UNSUPPORTED_TIME_UNIT = "Unsupported time unit.";
 
   // UDTFAR
@@ -100,6 +103,16 @@ public final class LibraryUdfMessages {
   // UDAFPatternMatch, UDAFDTWMatch
   public static final String UNSUPPORTED_DATATYPE = "Unsupported datatype %s";
 
+  // ExactOrderStatistics
+  public static final String UNSUPPORTED_DATA_TYPE = "Unsupported data type: %s";
+
+  // UDAFQuantile
+  public static final String UNSUPPORTED_DATA_TYPE_IN_QUANTILE = "Unsupported data type";
+  public static final String QUANTILE_K_MUST_BE_AT_LEAST_100 =
+      "Size K has to be greater than or equal to 100.";
+  public static final String QUANTILE_RANK_MUST_BE_IN_RANGE =
+      "rank has to be greater than 0 and less than or equal to 1.";
+
   // LinearScale
   public static final String DOMAIN_START_MUST_BE_LESS_THAN_DOMAIN_END =
       "domainStart must be less than domainEnd";
diff --git a/library-udf/src/main/i18n/zh/org/apache/iotdb/library/i18n/LibraryUdfMessages.java b/library-udf/src/main/i18n/zh/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
index 010b3d4fbb3..b237d924498 100644
--- a/library-udf/src/main/i18n/zh/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
+++ b/library-udf/src/main/i18n/zh/org/apache/iotdb/library/i18n/LibraryUdfMessages.java
@@ -57,6 +57,9 @@ public final class LibraryUdfMessages {
   public static final String ALL_VALUES_ARE_NAN = "所有值均为 NaN";
 
   // UDFEnvelopeAnalysis
+  public static final String FREQUENCY_MUST_BE_POSITIVE = "参数 'frequency' 必须大于 0。";
+  public static final String AMPLIFICATION_MUST_BE_AT_LEAST_1 =
+      "参数 'amplification' 必须大于或等于 1。";
   public static final String UNSUPPORTED_TIME_UNIT = "不支持的时间单位。";
 
   // UDTFAR
@@ -91,6 +94,15 @@ public final class LibraryUdfMessages {
   // UDAFPatternMatch, UDAFDTWMatch
   public static final String UNSUPPORTED_DATATYPE = "不支持的数据类型 %s";
 
+  // ExactOrderStatistics
+  public static final String UNSUPPORTED_DATA_TYPE = "不支持的数据类型:%s";
+
+  // UDAFQuantile
+  public static final String UNSUPPORTED_DATA_TYPE_IN_QUANTILE = "不支持的数据类型";
+  public static final String QUANTILE_K_MUST_BE_AT_LEAST_100 = "K 的大小必须大于或等于 100。";
+  public static final String QUANTILE_RANK_MUST_BE_IN_RANGE =
+      "rank 必须大于 0 且小于或等于 1。";
+
   // LinearScale
   public static final String DOMAIN_START_MUST_BE_LESS_THAN_DOMAIN_END =
       "domainStart 必须小于 domainEnd";
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java
index 1d63d040d87..4de6980dd74 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java
@@ -21,7 +21,7 @@ package org.apache.iotdb.library.anomaly;
 
 import org.apache.iotdb.library.util.CircularQueue;
 import org.apache.iotdb.library.util.LongCircularQueue;
-import org.apache.iotdb.library.util.Util;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -42,6 +42,9 @@ public class UDTFKSigma implements UDTF {
   private CircularQueue<Object> v;
   private LongCircularQueue t;
   private Type dataType;
+  private TypeServices.RowValueReader valueReader;
+  private TypeServices.NumericRowReader numericReader;
+  private TypeServices.RowValueWriter valueWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -66,6 +69,9 @@ public class UDTFKSigma implements UDTF {
         .setOutputDataType(udfParameters.getDataType(0));
     this.multipleK = udfParameters.getDoubleOrDefault("k", 3);
     this.dataType = udfParameters.getDataType(0);
+    valueReader = TypeServices.ROW_VALUE_READER_SERVICE.call(TypeServices.toReadType(dataType));
+    numericReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(TypeServices.toReadType(dataType));
+    valueWriter = TypeServices.rowValueWriter(dataType);
     this.windowSize = udfParameters.getIntOrDefault("window", 10000);
     this.v = new CircularQueue<>(windowSize);
     this.t = new LongCircularQueue(windowSize);
@@ -73,33 +79,12 @@ public class UDTFKSigma implements UDTF {
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    double value = Util.getValueAsDouble(row);
+    double value = numericReader.read(row);
     long timestamp = row.getTime();
     if (Double.isFinite(value) && !Double.isNaN(value)) {
       if (v.isFull()) {
         final double frontValue = Double.parseDouble(v.pop().toString());
-        switch (dataType) {
-          case INT32:
-            v.push(row.getInt(0));
-            break;
-          case INT64:
-            v.push(row.getLong(0));
-            break;
-          case DOUBLE:
-            v.push(row.getDouble(0));
-            break;
-          case FLOAT:
-            v.push(row.getFloat(0));
-            break;
-          case TIMESTAMP:
-          case DATE:
-          case TEXT:
-          case STRING:
-          case BLOB:
-          case BOOLEAN:
-          default:
-            break;
-        }
+        v.push(valueReader.read(row));
         t.pop();
         t.push(timestamp);
         this.sumX1 = this.sumX1 - frontValue + value;
@@ -108,31 +93,10 @@ public class UDTFKSigma implements UDTF {
         this.variance = this.sumX2 / v.getSize() - this.mean * this.mean;
         if (Math.abs(value - mean)
             > multipleK * Math.sqrt(this.variance * v.getSize() / (v.getSize() - 1))) {
-          Util.putValue(collector, dataType, timestamp, Util.getValueAsObject(row));
+          valueWriter.write(collector, timestamp, valueReader.read(row));
         }
       } else {
-        switch (dataType) {
-          case INT32:
-            v.push(row.getInt(0));
-            break;
-          case INT64:
-            v.push(row.getLong(0));
-            break;
-          case DOUBLE:
-            v.push(row.getDouble(0));
-            break;
-          case FLOAT:
-            v.push(row.getFloat(0));
-            break;
-          case BLOB:
-          case BOOLEAN:
-          case STRING:
-          case TEXT:
-          case DATE:
-          case TIMESTAMP:
-          default:
-            break;
-        }
+        v.push(valueReader.read(row));
         t.push(timestamp);
         this.sumX1 = this.sumX1 + value;
         this.sumX2 = this.sumX2 + value * value;
@@ -144,7 +108,7 @@ public class UDTFKSigma implements UDTF {
             Object vi = this.v.get(i);
             timestamp = this.t.get(i);
             if (Math.abs(Double.parseDouble(vi.toString()) - mean) > multipleK * stddev) {
-              Util.putValue(collector, dataType, timestamp, vi);
+              valueWriter.write(collector, timestamp, vi);
             }
           }
         }
@@ -160,7 +124,7 @@ public class UDTFKSigma implements UDTF {
         Object vi = this.v.get(i);
         long timestamp = this.t.get(i);
         if (Math.abs(Double.parseDouble(vi.toString()) - mean) > multipleK * stddev) {
-          Util.putValue(collector, dataType, timestamp, vi);
+          valueWriter.write(collector, timestamp, vi);
         }
       }
     }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java
index 783162aab69..f20d9737b1b 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.library.anomaly;
 
-import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -27,14 +27,14 @@ import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
-import org.apache.iotdb.udf.api.exception.UDFException;
 import org.apache.iotdb.udf.api.type.Type;
 
 /** This function is used to detect range anomaly of time series. */
 public class UDTFRange implements UDTF {
-  private Type dataType;
   private double upperBound;
   private double lowerBound;
+  private TypeServices.NumericRowReader rowReader;
+  private TypeServices.NumericRowWriter rowWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -51,50 +51,17 @@ public class UDTFRange implements UDTF {
         .setOutputDataType(parameters.getDataType(0));
     this.lowerBound = parameters.getDouble("lower_bound");
     this.upperBound = parameters.getDouble("upper_bound");
-    this.dataType = parameters.getDataType(0);
+    Type dataType = parameters.getDataType(0);
+    org.apache.tsfile.read.common.type.Type type = TypeServices.toReadType(dataType);
+    rowReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(type);
+    rowWriter = TypeServices.NUMERIC_ROW_WRITER_SERVICE.call(type);
   }
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    int intValue;
-    long longValue;
-    float floatValue;
-    double doubleValue;
-    long timestamp;
-    timestamp = row.getTime();
-    switch (dataType) {
-      case INT32:
-        intValue = row.getInt(0);
-        if (intValue > upperBound || intValue < lowerBound) {
-          collector.putInt(timestamp, intValue);
-        }
-        break;
-      case INT64:
-        longValue = row.getLong(0);
-        if (longValue > upperBound || longValue < lowerBound) {
-          collector.putLong(timestamp, longValue);
-        }
-        break;
-      case FLOAT:
-        floatValue = row.getFloat(0);
-        if (floatValue > upperBound || floatValue < lowerBound) {
-          collector.putFloat(timestamp, floatValue);
-        }
-        break;
-      case DOUBLE:
-        doubleValue = row.getDouble(0);
-        if (doubleValue > upperBound || doubleValue < lowerBound) {
-          collector.putDouble(timestamp, doubleValue);
-        }
-        break;
-      case DATE:
-      case TIMESTAMP:
-      case TEXT:
-      case STRING:
-      case BOOLEAN:
-      case BLOB:
-      default:
-        throw new UDFException(LibraryUdfMessages.NO_SUCH_DATA_TYPE);
+    double value = rowReader.read(row);
+    if (value > upperBound || value < lowerBound) {
+      rowWriter.write(row, collector);
     }
   }
 
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java
index a772861f2d8..f1cfe57a17f 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java
@@ -20,7 +20,7 @@
 package org.apache.iotdb.library.anomaly;
 
 import org.apache.iotdb.library.anomaly.util.WindowDetect;
-import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.RowWindow;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -28,7 +28,6 @@ import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
-import org.apache.iotdb.udf.api.exception.UDFException;
 import org.apache.iotdb.udf.api.type.Type;
 
 /**
@@ -38,6 +37,7 @@ import org.apache.iotdb.udf.api.type.Type;
 public class UDTFTwoSidedFilter implements UDTF {
   private double len;
   private double threshold;
+  private TypeServices.NumericWindowWriter windowWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -54,6 +54,9 @@ public class UDTFTwoSidedFilter implements UDTF {
         .setOutputDataType(parameters.getDataType(0));
     this.len = parameters.getDoubleOrDefault("len", 5);
     this.threshold = parameters.getDoubleOrDefault("threshold", 0.4);
+    windowWriter =
+        TypeServices.NUMERIC_WINDOW_WRITER_SERVICE.call(
+            TypeServices.toReadType(parameters.getDataType(0)));
   }
 
   @Override
@@ -61,36 +64,7 @@ public class UDTFTwoSidedFilter implements UDTF {
     WindowDetect wd = new WindowDetect(rowWindow.getRowIterator(), len, threshold);
     double[] repaired = wd.getRepaired();
     long[] time = wd.getTime();
-    switch (rowWindow.getDataType(0)) {
-      case DOUBLE:
-        for (int i = 0; i < time.length; i++) {
-          collector.putDouble(time[i], repaired[i]);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < time.length; i++) {
-          collector.putFloat(time[i], (float) repaired[i]);
-        }
-        break;
-      case INT32:
-        for (int i = 0; i < time.length; i++) {
-          collector.putInt(time[i], (int) Math.round(repaired[i]));
-        }
-        break;
-      case INT64:
-        for (int i = 0; i < time.length; i++) {
-          collector.putLong(time[i], Math.round(repaired[i]));
-        }
-        break;
-      case BOOLEAN:
-      case BLOB:
-      case STRING:
-      case TEXT:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        throw new UDFException(LibraryUdfMessages.NO_SUCH_DATA_TYPE);
-    }
+    windowWriter.write(time, repaired, collector);
   }
 
   @Override
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java
index d3baa6a7bf0..cab15d5244d 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java
@@ -21,7 +21,7 @@ package org.apache.iotdb.library.dprofile;
 
 import org.apache.iotdb.library.dprofile.util.ExactOrderStatistics;
 import org.apache.iotdb.library.dprofile.util.GKArray;
-import org.apache.iotdb.library.util.Util;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -44,7 +44,8 @@ public class UDAFPercentile implements UDTF {
   private GKArray sketch;
   private boolean exact;
   private double rank;
-  private Type dataType;
+  private TypeServices.NumericRowReader rowReader;
+  private PercentileOperations operations;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -67,7 +68,17 @@ public class UDAFPercentile implements UDTF {
     configurations
         .setAccessStrategy(new RowByRowAccessStrategy())
         .setOutputDataType(parameters.getDataType(0));
-    dataType = parameters.getDataType(0);
+    Type dataType = parameters.getDataType(0);
+    org.apache.tsfile.read.common.type.Type type = TypeServices.toReadType(dataType);
+    rowReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(type);
+    operations =
+        TypeServices.numericService(
+                INT_OPERATIONS,
+                LONG_OPERATIONS,
+                FLOAT_OPERATIONS,
+                DOUBLE_OPERATIONS,
+                UNSUPPORTED_OPERATIONS)
+            .call(type);
     double error = parameters.getDoubleOrDefault("error", 0);
     rank = parameters.getDoubleOrDefault("rank", 0.5);
     exact = (error == 0);
@@ -76,120 +87,123 @@ public class UDAFPercentile implements UDTF {
     } else {
       sketch = new GKArray(error);
     }
-    switch (dataType) {
-      case INT32:
-        intDic = new HashMap<>();
-        break;
-      case INT64:
-        longDic = new HashMap<>();
-        break;
-      case FLOAT:
-        floatDic = new HashMap<>();
-        break;
-      case DOUBLE:
-        doubleDic = new HashMap<>();
-        break;
-      case TIMESTAMP:
-      case DATE:
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case BOOLEAN:
-      default:
-        break;
-    }
+    operations.initialize();
   }
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
     if (exact) {
       statistics.insert(row);
-      switch (dataType) {
-        case INT32:
-          intDic.put(row.getInt(0), row.getTime());
-          break;
-        case INT64:
-          longDic.put(row.getLong(0), row.getTime());
-          break;
-        case FLOAT:
-          floatDic.put(row.getFloat(0), row.getTime());
-          break;
-        case DOUBLE:
-          doubleDic.put(row.getDouble(0), row.getTime());
-          break;
-        case BLOB:
-        case BOOLEAN:
-        case STRING:
-        case TEXT:
-        case DATE:
-        case TIMESTAMP:
-        default:
-          break;
-      }
+      operations.insertExact(row);
     } else {
-      double res = Util.getValueAsDouble(row);
-      sketch.insert(res);
+      sketch.insert(rowReader.read(row));
     }
   }
 
   @Override
   public void terminate(PointCollector collector) throws Exception {
     if (exact) {
-      long time;
-      switch (dataType) {
-        case INT32:
-          int ires = Integer.parseInt(statistics.getPercentile(rank));
-          time = intDic.getOrDefault(ires, 0L);
-          collector.putInt(time, ires);
-          break;
-        case INT64:
-          long lres = Long.parseLong(statistics.getPercentile(rank));
-          time = longDic.getOrDefault(lres, 0L);
-          collector.putLong(time, lres);
-          break;
-        case FLOAT:
-          float fres = Float.parseFloat(statistics.getPercentile(rank));
-          time = floatDic.getOrDefault(fres, 0L);
-          collector.putFloat(time, fres);
-          break;
-        case DOUBLE:
-          double dres = Double.parseDouble(statistics.getPercentile(rank));
-          time = doubleDic.getOrDefault(dres, 0L);
-          collector.putDouble(time, dres);
-          break;
-        case DATE:
-        case TIMESTAMP:
-        case TEXT:
-        case STRING:
-        case BOOLEAN:
-        case BLOB:
-        default:
-          break;
-      }
+      operations.writeExact(this, collector);
     } else {
       double res = sketch.query(rank);
-      switch (dataType) {
-        case INT32:
-          collector.putInt(0, (int) res);
-          break;
-        case INT64:
-          collector.putLong(0, (long) res);
-          break;
-        case FLOAT:
-          collector.putFloat(0, (float) res);
-          break;
-        case DOUBLE:
-          collector.putDouble(0, res);
-          break;
-        case BOOLEAN:
-        case BLOB:
-        case STRING:
-        case TEXT:
-        case TIMESTAMP:
-        case DATE:
-        default:
-          break;
-      }
+      operations.writeApprox(res, collector);
     }
   }
+
+  private static final PercentileOperations INT_OPERATIONS =
+      new PercentileOperations() {
+        public void initialize() {
+          intDic = new HashMap<>();
+        }
+
+        public void insertExact(Row row) throws Exception {
+          intDic.put(row.getInt(0), row.getTime());
+        }
+
+        public void writeExact(UDAFPercentile target, PointCollector collector) throws Exception {
+          int value = Integer.parseInt(target.statistics.getPercentile(target.rank));
+          collector.putInt(intDic.getOrDefault(value, 0L), value);
+        }
+
+        public void writeApprox(double value, PointCollector collector) throws Exception {
+          collector.putInt(0, (int) value);
+        }
+      };
+  private static final PercentileOperations LONG_OPERATIONS =
+      new PercentileOperations() {
+        public void initialize() {
+          longDic = new HashMap<>();
+        }
+
+        public void insertExact(Row row) throws Exception {
+          longDic.put(row.getLong(0), row.getTime());
+        }
+
+        public void writeExact(UDAFPercentile target, PointCollector collector) throws Exception {
+          long value = Long.parseLong(target.statistics.getPercentile(target.rank));
+          collector.putLong(longDic.getOrDefault(value, 0L), value);
+        }
+
+        public void writeApprox(double value, PointCollector collector) throws Exception {
+          collector.putLong(0, (long) value);
+        }
+      };
+  private static final PercentileOperations FLOAT_OPERATIONS =
+      new PercentileOperations() {
+        public void initialize() {
+          floatDic = new HashMap<>();
+        }
+
+        public void insertExact(Row row) throws Exception {
+          floatDic.put(row.getFloat(0), row.getTime());
+        }
+
+        public void writeExact(UDAFPercentile target, PointCollector collector) throws Exception {
+          float value = Float.parseFloat(target.statistics.getPercentile(target.rank));
+          collector.putFloat(floatDic.getOrDefault(value, 0L), value);
+        }
+
+        public void writeApprox(double value, PointCollector collector) throws Exception {
+          collector.putFloat(0, (float) value);
+        }
+      };
+  private static final PercentileOperations DOUBLE_OPERATIONS =
+      new PercentileOperations() {
+        public void initialize() {
+          doubleDic = new HashMap<>();
+        }
+
+        public void insertExact(Row row) throws Exception {
+          doubleDic.put(row.getDouble(0), row.getTime());
+        }
+
+        public void writeExact(UDAFPercentile target, PointCollector collector) throws Exception {
+          double value = Double.parseDouble(target.statistics.getPercentile(target.rank));
+          collector.putDouble(doubleDic.getOrDefault(value, 0L), value);
+        }
+
+        public void writeApprox(double value, PointCollector collector) throws Exception {
+          collector.putDouble(0, value);
+        }
+      };
+  private static final PercentileOperations UNSUPPORTED_OPERATIONS =
+      new PercentileOperations() {
+        public void initialize() {}
+
+        public void insertExact(Row row) {}
+
+        public void writeExact(UDAFPercentile target, PointCollector collector) {}
+
+        public void writeApprox(double value, PointCollector collector) {}
+      };
+
+  private interface PercentileOperations {
+    void initialize();
+
+    void insertExact(Row row) throws Exception;
+
+    void writeExact(UDAFPercentile target, PointCollector collector) throws Exception;
+
+    void writeApprox(double value, PointCollector collector) throws Exception;
+  }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFQuantile.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFQuantile.java
index 0518e7a3d76..e2e13ebe525 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFQuantile.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFQuantile.java
@@ -19,7 +19,8 @@
 
 package org.apache.iotdb.library.dprofile;
 
-import org.apache.iotdb.library.util.Util;
+import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -29,11 +30,68 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.type.Type;
 
+import java.io.IOException;
+
 /** calculate the approximate percentile. */
 public class UDAFQuantile implements UDTF {
   private org.apache.iotdb.library.dprofile.util.HeapLongKLLSketch sketch;
   private double rank;
-  private Type dataType;
+  private QuantileOperations operations;
+
+  private static final QuantileOperations INT_OPERATIONS =
+      new QuantileOperations() {
+        public long encode(Row row) throws IOException {
+          return row.getInt(0);
+        }
+
+        public void write(long result, PointCollector collector) throws IOException {
+          collector.putInt(0, (int) result);
+        }
+      };
+  private static final QuantileOperations LONG_OPERATIONS =
+      new QuantileOperations() {
+        public long encode(Row row) throws IOException {
+          return row.getLong(0);
+        }
+
+        public void write(long result, PointCollector collector) throws IOException {
+          collector.putLong(0, result);
+        }
+      };
+  private static final QuantileOperations FLOAT_OPERATIONS =
+      new QuantileOperations() {
+        public long encode(Row row) throws IOException {
+          float value = row.getFloat(0);
+          long bits = Float.floatToIntBits(value);
+          return value >= 0f ? bits : bits ^ Long.MAX_VALUE;
+        }
+
+        public void write(long result, PointCollector collector) throws IOException {
+          result = (result >>> 31) == 0 ? result : result ^ Long.MAX_VALUE;
+          collector.putFloat(0, Float.intBitsToFloat((int) result));
+        }
+      };
+  private static final QuantileOperations DOUBLE_OPERATIONS =
+      new QuantileOperations() {
+        public long encode(Row row) throws IOException {
+          double value = row.getDouble(0);
+          long bits = Double.doubleToLongBits(value);
+          return value >= 0d ? bits : bits ^ Long.MAX_VALUE;
+        }
+
+        public void write(long result, PointCollector collector) throws IOException {
+          result = (result >>> 63) == 0 ? result : result ^ Long.MAX_VALUE;
+          collector.putDouble(0, Double.longBitsToDouble(result));
+        }
+      };
+  private static final QuantileOperations UNSUPPORTED_OPERATIONS =
+      new QuantileOperations() {
+        public long encode(Row row) {
+          throw new IllegalArgumentException(LibraryUdfMessages.UNSUPPORTED_DATA_TYPE_IN_QUANTILE);
+        }
+
+        public void write(long result, PointCollector collector) {}
+      };
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -42,11 +100,11 @@ public class UDAFQuantile implements UDTF {
         .validateInputSeriesDataType(0, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE)
         .validate(
             k -> (int) k >= 100,
-            "Size K has to be greater or equal than 100.",
+            LibraryUdfMessages.QUANTILE_K_MUST_BE_AT_LEAST_100,
             validator.getParameters().getIntOrDefault("K", 800))
         .validate(
             rank -> (double) rank > 0 && (double) rank <= 1,
-            "rank has to be greater than 0 and less than or equal to 1.",
+            LibraryUdfMessages.QUANTILE_RANK_MUST_BE_IN_RANGE,
             validator.getParameters().getDoubleOrDefault("rank", 0.5));
   }
 
@@ -56,7 +114,15 @@ public class UDAFQuantile implements UDTF {
     configurations
         .setAccessStrategy(new RowByRowAccessStrategy())
         .setOutputDataType(parameters.getDataType(0));
-    dataType = parameters.getDataType(0);
+    Type dataType = parameters.getDataType(0);
+    operations =
+        TypeServices.numericService(
+                INT_OPERATIONS,
+                LONG_OPERATIONS,
+                FLOAT_OPERATIONS,
+                DOUBLE_OPERATIONS,
+                UNSUPPORTED_OPERATIONS)
+            .call(TypeServices.toReadType(dataType));
     int k = parameters.getIntOrDefault("K", 800);
     rank = parameters.getDoubleOrDefault("rank", 0.5);
 
@@ -65,19 +131,7 @@ public class UDAFQuantile implements UDTF {
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    final long encoded;
-    switch (dataType) {
-      case INT32:
-        encoded = row.getInt(0);
-        break;
-      case INT64:
-        encoded = row.getLong(0);
-        break;
-      default:
-        encoded = dataToLong(Util.getValueAsDouble(row));
-        break;
-    }
-    sketch.update(encoded);
+    sketch.update(operations.encode(row));
   }
 
   @Override
@@ -94,49 +148,12 @@ public class UDAFQuantile implements UDTF {
         k = n - 1;
       }
     }
-    long result = sketch.findMinValueWithRank(k);
-    switch (dataType) {
-      case INT32:
-        collector.putInt(0, (int) result);
-        break;
-      case INT64:
-        collector.putLong(0, result);
-        break;
-      case FLOAT:
-        collector.putFloat(0, (float) longToResult(result));
-        break;
-      case DOUBLE:
-        collector.putDouble(0, longToResult(result));
-        break;
-      default:
-        break;
-    }
+    operations.write(sketch.findMinValueWithRank(k), collector);
   }
 
-  private long dataToLong(double res) {
-    switch (dataType) {
-      case FLOAT:
-        float f = (float) res;
-        long flBits = Float.floatToIntBits(f);
-        return f >= 0f ? flBits : flBits ^ Long.MAX_VALUE;
-      case DOUBLE:
-        long d = Double.doubleToLongBits(res);
-        return res >= 0d ? d : d ^ Long.MAX_VALUE;
-      default:
-        return (long) res;
-    }
-  }
+  private interface QuantileOperations {
+    long encode(Row row) throws IOException;
 
-  private double longToResult(long result) {
-    switch (dataType) {
-      case FLOAT:
-        result = (result >>> 31) == 0 ? result : result ^ Long.MAX_VALUE;
-        return Float.intBitsToFloat((int) (result));
-      case DOUBLE:
-        result = (result >>> 63) == 0 ? result : result ^ Long.MAX_VALUE;
-        return Double.longBitsToDouble(result);
-      default:
-        return (double) result;
-    }
+    void write(long result, PointCollector collector) throws IOException;
   }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java
index bc7d9ad7c84..5c947a846d1 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.library.dprofile;
 
 import org.apache.iotdb.library.util.NoNumberException;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -45,7 +46,27 @@ public class UDAFSpread implements UDTF {
   float floatMax = -Float.MAX_VALUE;
   double doubleMin = Double.MAX_VALUE;
   double doubleMax = -Double.MAX_VALUE;
-  Type dataType;
+  private SpreadTransformer transformer;
+  private SpreadTerminator terminator;
+
+  private static final SpreadTransformer INT_TRANSFORMER = UDAFSpread::transformInt;
+  private static final SpreadTransformer LONG_TRANSFORMER = UDAFSpread::transformLong;
+  private static final SpreadTransformer FLOAT_TRANSFORMER = UDAFSpread::transformFloat;
+  private static final SpreadTransformer DOUBLE_TRANSFORMER = UDAFSpread::transformDouble;
+  private static final SpreadTransformer UNSUPPORTED_TRANSFORMER = (target, row) -> {};
+
+  private static final SpreadTerminator INT_TERMINATOR =
+      (target, collector) -> collector.putInt(0, target.intMax - target.intMin);
+  private static final SpreadTerminator LONG_TERMINATOR =
+      (target, collector) -> collector.putLong(0, target.longMax - target.longMin);
+  private static final SpreadTerminator FLOAT_TERMINATOR =
+      (target, collector) -> collector.putFloat(0, target.floatMax - target.floatMin);
+  private static final SpreadTerminator DOUBLE_TERMINATOR =
+      (target, collector) -> collector.putDouble(0, target.doubleMax - target.doubleMin);
+  private static final SpreadTerminator UNSUPPORTED_TERMINATOR =
+      (target, collector) -> {
+        throw new NoNumberException();
+      };
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -57,60 +78,35 @@ public class UDAFSpread implements UDTF {
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws Exception {
-    dataType = parameters.getDataType(0);
+    Type dataType = parameters.getDataType(0);
     configurations.setAccessStrategy(new RowByRowAccessStrategy()).setOutputDataType(dataType);
+    org.apache.tsfile.read.common.type.Type type = TypeServices.toReadType(dataType);
+    transformer =
+        TypeServices.numericService(
+                INT_TRANSFORMER,
+                LONG_TRANSFORMER,
+                FLOAT_TRANSFORMER,
+                DOUBLE_TRANSFORMER,
+                UNSUPPORTED_TRANSFORMER)
+            .call(type);
+    terminator =
+        TypeServices.numericService(
+                INT_TERMINATOR,
+                LONG_TERMINATOR,
+                FLOAT_TERMINATOR,
+                DOUBLE_TERMINATOR,
+                UNSUPPORTED_TERMINATOR)
+            .call(type);
   }
 
   @Override
   public void transform(Row row, PointCollector pc) throws Exception {
-    switch (dataType) {
-      case INT32:
-        transformInt(row);
-        break;
-      case INT64:
-        transformLong(row);
-        break;
-      case FLOAT:
-        transformFloat(row);
-        break;
-      case DOUBLE:
-        transformDouble(row);
-        break;
-      case BLOB:
-      case TIMESTAMP:
-      case BOOLEAN:
-      case STRING:
-      case TEXT:
-      case DATE:
-      default:
-        break;
-    }
+    transformer.transform(this, row);
   }
 
   @Override
   public void terminate(PointCollector pc) throws Exception {
-    switch (dataType) {
-      case INT32:
-        pc.putInt(0, intMax - intMin);
-        break;
-      case INT64:
-        pc.putLong(0, longMax - longMin);
-        break;
-      case FLOAT:
-        pc.putFloat(0, floatMax - floatMin);
-        break;
-      case DOUBLE:
-        pc.putDouble(0, doubleMax - doubleMin);
-        break;
-      case TEXT:
-      case DATE:
-      case STRING:
-      case BOOLEAN:
-      case TIMESTAMP:
-      case BLOB:
-      default:
-        throw new NoNumberException();
-    }
+    terminator.terminate(this, pc);
   }
 
   private void transformInt(Row row) throws IOException {
@@ -140,4 +136,15 @@ public class UDAFSpread implements UDTF {
       doubleMax = Math.max(doubleMax, v);
     }
   }
+
+  @FunctionalInterface
+  private interface SpreadTransformer {
+    void transform(UDAFSpread target, Row row) throws IOException;
+  }
+
+  @FunctionalInterface
+  private interface SpreadTerminator {
+    void terminate(UDAFSpread target, PointCollector collector)
+        throws IOException, NoNumberException;
+  }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java
index 1698164a944..55e4deff22b 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.library.dprofile;
 
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -50,7 +51,7 @@ public class UDTFDistinct implements UDTF {
   private DoubleHashSet doubleSet;
   private BooleanHashSet booleanSet;
   private HashSet<String> stringSet;
-  private Type dataType;
+  private DistinctOperations operations;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -66,146 +67,184 @@ public class UDTFDistinct implements UDTF {
     configurations
         .setAccessStrategy(new RowByRowAccessStrategy())
         .setOutputDataType(parameters.getDataType(0));
-    dataType = parameters.getDataType(0);
-    switch (dataType) {
-      case INT32:
-        intSet = new IntHashSet();
-        break;
-      case INT64:
-        longSet = new LongHashSet();
-        break;
-      case FLOAT:
-        floatSet = new FloatHashSet();
-        break;
-      case DOUBLE:
-        doubleSet = new DoubleHashSet();
-        break;
-      case TEXT:
-        stringSet = new HashSet<>();
-        break;
-      case BOOLEAN:
-        booleanSet = new BooleanHashSet();
-        break;
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        break;
-    }
+    Type dataType = parameters.getDataType(0);
+    operations =
+        TypeServices.scalarService(
+                BOOLEAN_OPERATIONS,
+                INT_OPERATIONS,
+                LONG_OPERATIONS,
+                FLOAT_OPERATIONS,
+                DOUBLE_OPERATIONS,
+                TEXT_OPERATIONS,
+                UNSUPPORTED_OPERATIONS)
+            .call(TypeServices.toReadType(dataType));
+    operations.initialize(this);
   }
 
   @Override
   public void transform(Row row, PointCollector pc) throws Exception {
-    switch (dataType) {
-      case INT32:
-        intSet.add(row.getInt(0));
-        break;
-      case INT64:
-        longSet.add(row.getLong(0));
-        break;
-      case FLOAT:
-        floatSet.add(row.getFloat(0));
-        break;
-      case DOUBLE:
-        doubleSet.add(row.getDouble(0));
-        break;
-      case TEXT:
-        stringSet.add(row.getString(0));
-        break;
-      case BOOLEAN:
-        booleanSet.add(row.getBoolean(0));
-        break;
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        break;
-    }
+    operations.add(this, row);
   }
 
   @Override
   public void terminate(PointCollector pc) throws Exception {
-    int i = 0;
-    switch (dataType) {
-      case INT32:
-        MutableIntIterator intIterator = intSet.intIterator();
-        while (intIterator.hasNext()) {
-          pc.putInt(i, intIterator.next());
-          i++;
-        }
-        break;
-      case INT64:
-        MutableLongIterator longIterator = longSet.longIterator();
-        while (longIterator.hasNext()) {
-          pc.putLong(i, longIterator.next());
-          i++;
-        }
-        break;
-      case FLOAT:
-        MutableFloatIterator floatIterator = floatSet.floatIterator();
-        while (floatIterator.hasNext()) {
-          pc.putFloat(i, floatIterator.next());
-          i++;
-        }
-        break;
-      case DOUBLE:
-        MutableDoubleIterator doubleIterator = doubleSet.doubleIterator();
-        while (doubleIterator.hasNext()) {
-          pc.putDouble(i, doubleIterator.next());
-          i++;
-        }
-        break;
-      case TEXT:
-        for (String s : stringSet) {
-          pc.putString(i, s);
-          i++;
-        }
-        break;
-      case BOOLEAN:
-        MutableBooleanIterator booleanIterator = booleanSet.booleanIterator();
-        while (booleanIterator.hasNext()) {
-          pc.putBoolean(i, booleanIterator.next());
-          i++;
-        }
-        break;
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        break;
-    }
+    operations.terminate(this, pc);
   }
 
   @Override
   public void beforeDestroy() {
-    switch (dataType) {
-      case INT32:
-        intSet.clear();
-        break;
-      case INT64:
-        longSet.clear();
-        break;
-      case FLOAT:
-        floatSet.clear();
-        break;
-      case DOUBLE:
-        doubleSet.clear();
-        break;
-      case TEXT:
-        stringSet.clear();
-        break;
-      case BOOLEAN:
-        booleanSet.clear();
-        break;
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        break;
-    }
+    operations.clear(this);
+  }
+
+  private static final DistinctOperations INT_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.intSet = new IntHashSet();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.intSet.add(row.getInt(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          MutableIntIterator iterator = target.intSet.intIterator();
+          int i = 0;
+          while (iterator.hasNext()) {
+            collector.putInt(i++, iterator.next());
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.intSet.clear();
+        }
+      };
+  private static final DistinctOperations LONG_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.longSet = new LongHashSet();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.longSet.add(row.getLong(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          MutableLongIterator iterator = target.longSet.longIterator();
+          int i = 0;
+          while (iterator.hasNext()) {
+            collector.putLong(i++, iterator.next());
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.longSet.clear();
+        }
+      };
+  private static final DistinctOperations FLOAT_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.floatSet = new FloatHashSet();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.floatSet.add(row.getFloat(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          MutableFloatIterator iterator = target.floatSet.floatIterator();
+          int i = 0;
+          while (iterator.hasNext()) {
+            collector.putFloat(i++, iterator.next());
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.floatSet.clear();
+        }
+      };
+  private static final DistinctOperations DOUBLE_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.doubleSet = new DoubleHashSet();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.doubleSet.add(row.getDouble(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          MutableDoubleIterator iterator = target.doubleSet.doubleIterator();
+          int i = 0;
+          while (iterator.hasNext()) {
+            collector.putDouble(i++, iterator.next());
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.doubleSet.clear();
+        }
+      };
+  private static final DistinctOperations BOOLEAN_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.booleanSet = new BooleanHashSet();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.booleanSet.add(row.getBoolean(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          MutableBooleanIterator iterator = target.booleanSet.booleanIterator();
+          int i = 0;
+          while (iterator.hasNext()) {
+            collector.putBoolean(i++, iterator.next());
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.booleanSet.clear();
+        }
+      };
+  private static final DistinctOperations TEXT_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {
+          target.stringSet = new HashSet<>();
+        }
+
+        public void add(UDTFDistinct target, Row row) throws Exception {
+          target.stringSet.add(row.getString(0));
+        }
+
+        public void terminate(UDTFDistinct target, PointCollector collector) throws Exception {
+          int i = 0;
+          for (String value : target.stringSet) {
+            collector.putString(i++, value);
+          }
+        }
+
+        public void clear(UDTFDistinct target) {
+          target.stringSet.clear();
+        }
+      };
+  private static final DistinctOperations UNSUPPORTED_OPERATIONS =
+      new DistinctOperations() {
+        public void initialize(UDTFDistinct target) {}
+
+        public void add(UDTFDistinct target, Row row) {}
+
+        public void terminate(UDTFDistinct target, PointCollector collector) {}
+
+        public void clear(UDTFDistinct target) {}
+      };
+
+  private interface DistinctOperations {
+    void initialize(UDTFDistinct target) throws Exception;
+
+    void add(UDTFDistinct target, Row row) throws Exception;
+
+    void terminate(UDTFDistinct target, PointCollector collector) throws Exception;
+
+    void clear(UDTFDistinct target);
   }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java
index 545d4096f51..27918e5238d 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java
@@ -20,8 +20,7 @@
 package org.apache.iotdb.library.dprofile;
 
 import org.apache.iotdb.library.dprofile.util.LTThreeBuckets;
-import org.apache.iotdb.library.util.NoNumberException;
-import org.apache.iotdb.library.util.Util;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.access.RowIterator;
@@ -58,6 +57,10 @@ public class UDTFSample implements UDTF {
   private int num = 0; // number of points already sampled
   private Random random;
   private Type dataType;
+  private TypeServices.NumericValueWriter numericValueWriter;
+  private TypeServices.NumericRowReader numericRowReader;
+  private TypeServices.RowValueReader valueReader;
+  private TypeServices.RowValueWriter valueWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -81,6 +84,12 @@ public class UDTFSample implements UDTF {
       throws Exception {
     this.k = parameters.getIntOrDefault("k", 1);
     this.dataType = parameters.getDataType(0);
+    numericValueWriter =
+        TypeServices.NUMERIC_VALUE_WRITER_SERVICE.call(TypeServices.toReadType(dataType));
+    numericRowReader =
+        TypeServices.NUMERIC_ROW_READER_SERVICE.call(TypeServices.toReadType(dataType));
+    valueReader = TypeServices.rowValueReader(dataType);
+    valueWriter = TypeServices.rowValueWriter(dataType);
     String methodIn = parameters.getStringOrDefault("method", METHOD_RESERVOIR);
     if ("triangle".equalsIgnoreCase(methodIn)) {
       this.method = Method.TRIANGLE;
@@ -112,7 +121,7 @@ public class UDTFSample implements UDTF {
       x = random.nextInt(num + 1);
     }
     if (x < this.k) {
-      Object v = Util.getValueAsObject(row);
+      Object v = valueReader.read(row);
       Long t = row.getTime();
       this.samples[x] = Pair.of(t, v);
     }
@@ -131,56 +140,35 @@ public class UDTFSample implements UDTF {
         for (int i = 0; i < n; i++) {
           Row row = rowWindow.getRow(i);
           long time = row.getTime();
-          double data = Util.getValueAsDouble(row);
+          double data = numericRowReader.read(row);
           input.add(Pair.of(time, data));
         }
         if (k > 2) {
           // The first and last element will always be sampled so the buckets is k - 2
           List<Pair<Long, Double>> output = LTThreeBuckets.sorted(input, k - 2);
           for (Pair<Long, Double> p : output) {
-            switch (dataType) {
-              case INT32:
-                collector.putInt(p.getLeft(), p.getRight().intValue());
-                break;
-              case INT64:
-                collector.putLong(p.getLeft(), p.getRight().longValue());
-                break;
-              case FLOAT:
-                collector.putFloat(p.getLeft(), p.getRight().floatValue());
-                break;
-              case DOUBLE:
-                collector.putDouble(p.getLeft(), p.getRight());
-                break;
-              case TIMESTAMP:
-              case DATE:
-              case BLOB:
-              case BOOLEAN:
-              case STRING:
-              case TEXT:
-              default:
-                throw new NoNumberException();
-            }
+            numericValueWriter.write(p.getLeft(), p.getRight(), collector);
           }
         } else { // For corner case of k == 1 and k == 2
           Row row = rowWindow.getRow(0); // Put first element
-          Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row));
+          valueWriter.write(collector, row.getTime(), valueReader.read(row));
           if (k == 2) {
             row = rowWindow.getRow(n - 1); // Put last element
-            Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row));
+            valueWriter.write(collector, row.getTime(), valueReader.read(row));
           }
         }
       } else { // Method.ISOMETRIC
         for (long i = 0; i < this.k; i++) {
           long j = Math.floorDiv(i * n, (long) k); // avoid intermediate result overflows
           Row row = rowWindow.getRow((int) j);
-          Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row));
+          valueWriter.write(collector, row.getTime(), valueReader.read(row));
         }
       }
     } else { // when k is larger than series length, output all points
       RowIterator iterator = rowWindow.getRowIterator();
       while (iterator.hasNextRow()) {
         Row row = iterator.next();
-        Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row));
+        valueWriter.write(collector, row.getTime(), valueReader.read(row));
       }
     }
   }
@@ -192,7 +180,7 @@ public class UDTFSample implements UDTF {
       Arrays.sort(samples, 0, m);
       for (int i = 0; i < m; i++) {
         Pair<Long, Object> p = samples[i];
-        Util.putValue(pc, dataType, p.getLeft(), p.getRight());
+        valueWriter.write(pc, p.getLeft(), p.getRight());
       }
     }
   }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java
index 47ca5e2b12f..7864c0a033c 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java
@@ -19,10 +19,13 @@
 
 package org.apache.iotdb.library.dprofile.util;
 
+import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
+import org.apache.tsfile.read.common.type.service.TypeService;
 import org.eclipse.collections.impl.list.mutable.primitive.DoubleArrayList;
 import org.eclipse.collections.impl.list.mutable.primitive.FloatArrayList;
 import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList;
@@ -30,6 +33,7 @@ import org.eclipse.collections.impl.list.mutable.primitive.LongArrayList;
 
 import java.io.IOException;
 import java.util.NoSuchElementException;
+import java.util.function.Function;
 
 /**
  * Util for computing median, MAD, percentile.
@@ -46,71 +50,24 @@ public class ExactOrderStatistics {
   private DoubleArrayList doubleArrayList;
   private IntArrayList intArrayList;
   private LongArrayList longArrayList;
+  private final StatisticsOperations operations;
 
   public ExactOrderStatistics(Type type) throws UDFInputSeriesDataTypeNotValidException {
     this.dataType = type;
-    switch (dataType) {
-      case INT32:
-        intArrayList = new IntArrayList();
-        break;
-      case INT64:
-        longArrayList = new LongArrayList();
-        break;
-      case FLOAT:
-        floatArrayList = new FloatArrayList();
-        break;
-      case DOUBLE:
-        doubleArrayList = new DoubleArrayList();
-        break;
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
+    try {
+      operations = OPERATIONS_SERVICE.call(TypeServices.toReadType(type)).apply(this);
+    } catch (IllegalArgumentException e) {
+      throw new UDFInputSeriesDataTypeNotValidException(
+          0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
     }
   }
 
   public void insert(Row row) throws UDFInputSeriesDataTypeNotValidException, IOException {
-    switch (dataType) {
-      case INT32:
-        intArrayList.add(row.getInt(0));
-        break;
-      case INT64:
-        longArrayList.add(row.getLong(0));
-        break;
-      case FLOAT:
-        float vf = row.getFloat(0);
-        if (Float.isFinite(vf)) {
-          floatArrayList.add(vf);
-        }
-        break;
-      case DOUBLE:
-        double vd = row.getDouble(0);
-        if (Double.isFinite(vd)) {
-          doubleArrayList.add(vd);
-        }
-        break;
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
-    }
+    operations.insert(row);
   }
 
   public double getMedian() throws UDFInputSeriesDataTypeNotValidException {
-    switch (dataType) {
-      case INT32:
-        return getMedian(intArrayList);
-      case INT64:
-        return getMedian(longArrayList);
-      case FLOAT:
-        return getMedian(floatArrayList);
-      case DOUBLE:
-        return getMedian(doubleArrayList);
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
-    }
+    return operations.median();
   }
 
   public static double getMedian(FloatArrayList nums) {
@@ -179,20 +136,7 @@ public class ExactOrderStatistics {
   }
 
   public double getMad() throws UDFInputSeriesDataTypeNotValidException {
-    switch (dataType) {
-      case INT32:
-        return getMad(intArrayList);
-      case INT64:
-        return getMad(longArrayList);
-      case FLOAT:
-        return getMad(floatArrayList);
-      case DOUBLE:
-        return getMad(doubleArrayList);
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
-    }
+    return operations.mad();
   }
 
   public static double getMad(DoubleArrayList nums) {
@@ -259,20 +203,118 @@ public class ExactOrderStatistics {
   }
 
   public String getPercentile(double phi) throws UDFInputSeriesDataTypeNotValidException {
-    switch (dataType) {
-      case INT32:
-        return Integer.toString(getPercentile(intArrayList, phi));
-      case INT64:
-        return Long.toString(getPercentile(longArrayList, phi));
-      case FLOAT:
-        return Float.toString(getPercentile(floatArrayList, phi));
-      case DOUBLE:
-        return Double.toString(getPercentile(doubleArrayList, phi));
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0, dataType, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);
-    }
+    return operations.percentile(phi);
+  }
+
+  private static final TypeService<Function<ExactOrderStatistics, StatisticsOperations>>
+      OPERATIONS_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32 ->
+                    target -> {
+                      target.intArrayList = new IntArrayList();
+                      return new StatisticsOperations() {
+                        public void insert(Row row) throws IOException {
+                          target.intArrayList.add(row.getInt(0));
+                        }
+
+                        public double median() {
+                          return getMedian(target.intArrayList);
+                        }
+
+                        public double mad() {
+                          return getMad(target.intArrayList);
+                        }
+
+                        public String percentile(double phi) {
+                          return Integer.toString(getPercentile(target.intArrayList, phi));
+                        }
+                      };
+                    };
+                case INT64 ->
+                    target -> {
+                      target.longArrayList = new LongArrayList();
+                      return new StatisticsOperations() {
+                        public void insert(Row row) throws IOException {
+                          target.longArrayList.add(row.getLong(0));
+                        }
+
+                        public double median() {
+                          return getMedian(target.longArrayList);
+                        }
+
+                        public double mad() {
+                          return getMad(target.longArrayList);
+                        }
+
+                        public String percentile(double phi) {
+                          return Long.toString(getPercentile(target.longArrayList, phi));
+                        }
+                      };
+                    };
+                case FLOAT ->
+                    target -> {
+                      target.floatArrayList = new FloatArrayList();
+                      return new StatisticsOperations() {
+                        public void insert(Row row) throws IOException {
+                          float value = row.getFloat(0);
+                          if (Float.isFinite(value)) {
+                            target.floatArrayList.add(value);
+                          }
+                        }
+
+                        public double median() {
+                          return getMedian(target.floatArrayList);
+                        }
+
+                        public double mad() {
+                          return getMad(target.floatArrayList);
+                        }
+
+                        public String percentile(double phi) {
+                          return Float.toString(getPercentile(target.floatArrayList, phi));
+                        }
+                      };
+                    };
+                case DOUBLE ->
+                    target -> {
+                      target.doubleArrayList = new DoubleArrayList();
+                      return new StatisticsOperations() {
+                        public void insert(Row row) throws IOException {
+                          double value = row.getDouble(0);
+                          if (Double.isFinite(value)) {
+                            target.doubleArrayList.add(value);
+                          }
+                        }
+
+                        public double median() {
+                          return getMedian(target.doubleArrayList);
+                        }
+
+                        public double mad() {
+                          return getMad(target.doubleArrayList);
+                        }
+
+                        public String percentile(double phi) {
+                          return Double.toString(getPercentile(target.doubleArrayList, phi));
+                        }
+                      };
+                    };
+                case BOOLEAN, TEXT, ROW, UNKNOWN, TIMESTAMP, DATE, BLOB, STRING, OBJECT, VECTOR ->
+                    target -> {
+                      throw new IllegalArgumentException(
+                          String.format(LibraryUdfMessages.UNSUPPORTED_DATA_TYPE, type));
+                    };
+              };
+
+  private interface StatisticsOperations {
+    void insert(Row row) throws IOException;
+
+    double median();
+
+    double mad();
+
+    String percentile(double phi);
   }
 
   public static int getPercentile(IntArrayList nums, double phi) {
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java
index 873754a1194..9605d5e4d16 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.library.drepair;
 
 import org.apache.iotdb.library.drepair.util.TimestampRepair;
 import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.library.util.Util;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.RowWindow;
@@ -37,6 +38,7 @@ public class UDTFTimestampRepair implements UDTF {
   String intervalMethod;
   long interval;
   long intervalMode;
+  private TypeServices.NumericWindowWriter windowWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -67,6 +69,9 @@ public class UDTFTimestampRepair implements UDTF {
     configurations
         .setAccessStrategy(new SlidingSizeWindowAccessStrategy(Integer.MAX_VALUE))
         .setOutputDataType(parameters.getDataType(0));
+    windowWriter =
+        TypeServices.NUMERIC_CAST_WINDOW_WRITER_SERVICE.call(
+            TypeServices.toReadType(parameters.getDataType(0)));
 
     intervalMethod = parameters.getStringOrDefault("method", "Median");
     String intervalString = parameters.getStringOrDefault("interval", null);
@@ -100,35 +105,6 @@ public class UDTFTimestampRepair implements UDTF {
     ts.dpRepair();
     long[] timestamp = ts.getRepaired();
     double[] value = ts.getRepairedValue();
-    switch (rowWindow.getDataType(0)) {
-      case DOUBLE:
-        for (int i = 0; i < timestamp.length; i++) {
-          collector.putDouble(timestamp[i], value[i]);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < timestamp.length; i++) {
-          collector.putFloat(timestamp[i], (float) value[i]);
-        }
-        break;
-      case INT32:
-        for (int i = 0; i < timestamp.length; i++) {
-          collector.putInt(timestamp[i], (int) value[i]);
-        }
-        break;
-      case INT64:
-        for (int i = 0; i < timestamp.length; i++) {
-          collector.putLong(timestamp[i], (long) value[i]);
-        }
-        break;
-      case DATE:
-      case TIMESTAMP:
-      case BLOB:
-      case BOOLEAN:
-      case TEXT:
-      case STRING:
-      default:
-        throw new UDFException("");
-    }
+    windowWriter.write(timestamp, value, collector);
   }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java
index 0b650015368..ed795ea0be1 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.library.drepair.util.PreviousFill;
 import org.apache.iotdb.library.drepair.util.ScreenFill;
 import org.apache.iotdb.library.drepair.util.ValueFill;
 import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.RowWindow;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -40,6 +41,7 @@ import org.apache.iotdb.udf.api.type.Type;
 /** This function is used to interpolate time series. */
 public class UDTFValueFill implements UDTF {
   private String method;
+  private TypeServices.NumericWindowWriter windowWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -55,6 +57,9 @@ public class UDTFValueFill implements UDTF {
         .setAccessStrategy(new SlidingSizeWindowAccessStrategy(Integer.MAX_VALUE))
         .setOutputDataType(parameters.getDataType(0));
     method = parameters.getStringOrDefault("method", "linear");
+    windowWriter =
+        TypeServices.NUMERIC_WINDOW_WRITER_SERVICE.call(
+            TypeServices.toReadType(parameters.getDataType(0)));
   }
 
   @Override
@@ -78,35 +83,6 @@ public class UDTFValueFill implements UDTF {
     vf.fill();
     double[] repaired = vf.getFilled();
     long[] time = vf.getTime();
-    switch (rowWindow.getDataType(0)) {
-      case DOUBLE:
-        for (int i = 0; i < time.length; i++) {
-          collector.putDouble(time[i], repaired[i]);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < time.length; i++) {
-          collector.putFloat(time[i], (float) repaired[i]);
-        }
-        break;
-      case INT32:
-        for (int i = 0; i < time.length; i++) {
-          collector.putInt(time[i], (int) Math.round(repaired[i]));
-        }
-        break;
-      case INT64:
-        for (int i = 0; i < time.length; i++) {
-          collector.putLong(time[i], Math.round(repaired[i]));
-        }
-        break;
-      case TEXT:
-      case STRING:
-      case BOOLEAN:
-      case BLOB:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        throw new UDFException("");
-    }
+    windowWriter.write(time, repaired, collector);
   }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java
index a4d9b18ef60..7a0a1990d0f 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.library.drepair.util.LsGreedy;
 import org.apache.iotdb.library.drepair.util.Screen;
 import org.apache.iotdb.library.drepair.util.ValueRepair;
 import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.RowWindow;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -40,6 +41,7 @@ public class UDTFValueRepair implements UDTF {
   double maxSpeed;
   double center;
   double sigma;
+  private TypeServices.NumericWindowWriter windowWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -68,6 +70,9 @@ public class UDTFValueRepair implements UDTF {
     maxSpeed = parameters.getDoubleOrDefault("maxSpeed", Double.NaN);
     center = parameters.getDoubleOrDefault("center", 0);
     sigma = parameters.getDoubleOrDefault("sigma", Double.NaN);
+    windowWriter =
+        TypeServices.NUMERIC_WINDOW_WRITER_SERVICE.call(
+            TypeServices.toReadType(parameters.getDataType(0)));
   }
 
   @Override
@@ -95,36 +100,6 @@ public class UDTFValueRepair implements UDTF {
     vr.repair();
     double[] repaired = vr.getRepaired();
     long[] time = vr.getTime();
-    switch (rowWindow.getDataType(0)) {
-      case DOUBLE:
-        for (int i = 0; i < time.length; i++) {
-          collector.putDouble(time[i], repaired[i]);
-        }
-        break;
-      case FLOAT:
-        for (int i = 0; i < time.length; i++) {
-          collector.putFloat(time[i], (float) repaired[i]);
-        }
-        break;
-      case INT32:
-        for (int i = 0; i < time.length; i++) {
-          collector.putInt(time[i], (int) Math.round(repaired[i]));
-        }
-        break;
-      case INT64:
-        for (int i = 0; i < time.length; i++) {
-          collector.putLong(time[i], Math.round(repaired[i]));
-        }
-        break;
-      case TIMESTAMP:
-      case DATE:
-      case BLOB:
-      case BOOLEAN:
-      case STRING:
-      case TEXT:
-
-      default:
-        throw new Exception();
-    }
+    windowWriter.write(time, repaired, collector);
   }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDFEnvelopeAnalysis.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDFEnvelopeAnalysis.java
index b542c8a3d51..430d960b562 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDFEnvelopeAnalysis.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDFEnvelopeAnalysis.java
@@ -20,6 +20,8 @@
 package org.apache.iotdb.library.frequency;
 
 import org.apache.iotdb.library.i18n.LibraryUdfMessages;
+import org.apache.iotdb.library.util.NoNumberException;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -46,6 +48,7 @@ public class UDFEnvelopeAnalysis implements UDTF {
   private String timestampPrecision;
   private final DoubleArrayList signals = new DoubleArrayList();
   private final LongArrayList timestamps = new LongArrayList();
+  private TypeServices.NumericRowReader valueReader;
   private static final String TIMESTAMP_PRECISION = "timestampPrecision";
   private static final String FREQUENCY = "frequency";
   private static final String AMPLIFICATION = "amplification";
@@ -60,11 +63,11 @@ public class UDFEnvelopeAnalysis implements UDTF {
         .validateInputSeriesDataType(0, Type.DOUBLE, Type.FLOAT, Type.INT32, Type.INT64)
         .validate(
             x -> (double) x > 0,
-            "The param 'frequency' must > 0.",
+            LibraryUdfMessages.FREQUENCY_MUST_BE_POSITIVE,
             validator.getParameters().getDoubleOrDefault(FREQUENCY, Double.MAX_VALUE))
         .validate(
             x -> (int) x >= 1,
-            "The param 'amplification' must >= 1.",
+            LibraryUdfMessages.AMPLIFICATION_MUST_BE_AT_LEAST_1,
             validator.getParameters().getIntOrDefault(AMPLIFICATION, 1));
   }
 
@@ -75,11 +78,14 @@ public class UDFEnvelopeAnalysis implements UDTF {
     frequency = parameters.getDoubleOrDefault(FREQUENCY, Double.MAX_VALUE);
     amplification = parameters.getIntOrDefault(AMPLIFICATION, 1);
     timestampPrecision = parameters.getSystemStringOrDefault(TIMESTAMP_PRECISION, MS_PRECISION);
+    valueReader =
+        TypeServices.NUMERIC_ROW_READER_SERVICE.call(
+            TypeServices.toReadType(parameters.getDataType(0)));
   }
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    signals.add(getValueAsDouble(row, 0));
+    signals.add(valueReader.read(row));
     if (timestamps.size() < 10) {
       timestamps.add(row.getTime());
     }
@@ -213,31 +219,14 @@ public class UDFEnvelopeAnalysis implements UDTF {
   }
 
   public double getValueAsDouble(Row row, int index) throws IOException {
-    double ans;
-    switch (row.getDataType(index)) {
-      case INT32:
-        ans = row.getInt(index);
-        break;
-      case INT64:
-        ans = row.getLong(index);
-        break;
-      case FLOAT:
-        ans = row.getFloat(index);
-        break;
-      case DOUBLE:
-        ans = row.getDouble(index);
-        break;
-      case TEXT:
-      case STRING:
-      case BOOLEAN:
-      case BLOB:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UDFOutputSeriesDataTypeNotValidException(
-            index, "Fail to get data type in row " + row.getTime());
+    try {
+      return TypeServices.NUMERIC_ROW_READER_SERVICE
+          .call(TypeServices.toReadType(row.getDataType(index)))
+          .read(row);
+    } catch (NoNumberException e) {
+      throw new UDFOutputSeriesDataTypeNotValidException(
+          index, LibraryUdfMessages.FAIL_TO_GET_DATA_TYPE_IN_ROW + row.getTime());
     }
-    return ans;
   }
 
   public static double calculateFrequencyByTimeUnit(long time, String timeUnit) {
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFDTWMatch.java b/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFDTWMatch.java
index 47646fd5fe9..f5aa0418471 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFDTWMatch.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFDTWMatch.java
@@ -19,9 +19,9 @@
 
 package org.apache.iotdb.library.match;
 
-import org.apache.iotdb.library.i18n.LibraryUdfMessages;
 import org.apache.iotdb.library.match.model.DTWMatchResult;
 import org.apache.iotdb.library.match.model.DTWState;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.State;
 import org.apache.iotdb.udf.api.UDAF;
 import org.apache.iotdb.udf.api.customizer.config.UDAFConfigurations;
@@ -44,12 +44,16 @@ public class UDAFDTWMatch implements UDAF {
   private Double[] pattern;
   private float threshold;
   private DTWState state;
+  private TypeServices.ColumnNumericReader valueReader;
 
   @Override
   public void beforeStart(UDFParameters udfParameters, UDAFConfigurations udafConfigurations) {
     udafConfigurations.setOutputDataType(Type.TEXT);
     Map<String, String> attributes = udfParameters.getAttributes();
     threshold = Float.parseFloat(attributes.get("threshold"));
+    valueReader =
+        TypeServices.COLUMN_NUMERIC_READER_SERVICE.call(
+            TypeServices.toReadType(udfParameters.getDataType(0)));
     pattern =
         Arrays.stream(attributes.get("pattern").split(","))
             .map(Double::valueOf)
@@ -81,7 +85,7 @@ public class UDAFDTWMatch implements UDAF {
       }
       if (!columns[1].isNull(i)) {
         long timestamp = columns[1].getLong(i);
-        double value = getValue(columns[0], i);
+        double value = valueReader.read(columns[0], i);
         DTWState.updateBuffer(timestamp, value);
         if (DTWState.getValueBuffer().length == pattern.length) {
           float dtw = calculateDTW(DTWState.getValueBuffer(), pattern);
@@ -95,24 +99,6 @@ public class UDAFDTWMatch implements UDAF {
     }
   }
 
-  private double getValue(Column column, int i) {
-    switch (column.getDataType()) {
-      case INT32:
-        return column.getInt(i);
-      case INT64:
-        return column.getLong(i);
-      case FLOAT:
-        return column.getFloat(i);
-      case DOUBLE:
-        return column.getDouble(i);
-      case BOOLEAN:
-        return column.getBoolean(i) ? 1.0D : 0.0D;
-      default:
-        throw new RuntimeException(
-            String.format(LibraryUdfMessages.UNSUPPORTED_DATATYPE, column.getDataType()));
-    }
-  }
-
   private float calculateDTW(Double[] series1, Double[] series2) {
     int n = series1.length;
     double[][] dtw = new double[n][n];
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFPatternMatch.java b/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFPatternMatch.java
index 89c902ab7f6..62fa387c67b 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFPatternMatch.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/match/UDAFPatternMatch.java
@@ -19,12 +19,12 @@
 
 package org.apache.iotdb.library.match;
 
-import org.apache.iotdb.library.i18n.LibraryUdfMessages;
 import org.apache.iotdb.library.match.model.DTWMatchResult;
 import org.apache.iotdb.library.match.model.PatternContext;
 import org.apache.iotdb.library.match.model.PatternResult;
 import org.apache.iotdb.library.match.model.PatternState;
 import org.apache.iotdb.library.match.model.Point;
+import org.apache.iotdb.library.util.TypeServices;
 import org.apache.iotdb.udf.api.State;
 import org.apache.iotdb.udf.api.UDAF;
 import org.apache.iotdb.udf.api.customizer.config.UDAFConfigurations;
@@ -54,12 +54,16 @@ public class UDAFPatternMatch implements UDAF {
   private Double[] valuePattern;
   private float threshold;
   private PatternState state;
+  private TypeServices.ColumnNumericReader valueReader;
 
   @Override
   public void beforeStart(UDFParameters udfParameters, UDAFConfigurations udafConfigurations) {
     udafConfigurations.setOutputDataType(Type.TEXT);
     Map<String, String> attributes = udfParameters.getAttributes();
     threshold = Float.parseFloat(attributes.get(THRESHOLD_PARAM));
+    valueReader =
+        TypeServices.COLUMN_NUMERIC_READER_SERVICE.call(
+            TypeServices.toReadType(udfParameters.getDataType(0)));
   }
 
   @Override
@@ -79,7 +83,7 @@ public class UDAFPatternMatch implements UDAF {
       }
       if (!columns[1].isNull(i)) {
         long timestamp = columns[1].getLong(i);
-        double value = getValue(columns[0], i);
+        double value = valueReader.read(columns[0], i);
         matchState.updateBuffer(timestamp, value);
       }
     }
@@ -175,22 +179,4 @@ public class UDAFPatternMatch implements UDAF {
             timePattern,
             valuePattern);
   }
-
-  private double getValue(Column column, int i) {
-    switch (column.getDataType()) {
-      case INT32:
-        return column.getInt(i);
-      case INT64:
-        return column.getLong(i);
-      case FLOAT:
-        return column.getFloat(i);
-      case DOUBLE:
-        return column.getDouble(i);
-      case BOOLEAN:
-        return column.getBoolean(i) ? 1.0D : 0.0D;
-      default:
-        throw new RuntimeException(
-            String.format(LibraryUdfMessages.UNSUPPORTED_DATATYPE, column.getDataType()));
-    }
-  }
 }
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/util/TypeServices.java b/library-udf/src/main/java/org/apache/iotdb/library/util/TypeServices.java
new file mode 100644
index 00000000000..9081936eba4
--- /dev/null
+++ b/library-udf/src/main/java/org/apache/iotdb/library/util/TypeServices.java
@@ -0,0 +1,300 @@
+/*
+ * 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.library.util;
+
+import org.apache.iotdb.udf.api.access.Row;
+import org.apache.iotdb.udf.api.collector.PointCollector;
+import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.service.TypeService;
+
+import java.io.IOException;
+
+/** Type-specific operations shared by library UDFs. */
+public final class TypeServices {
+
+  public static final TypeService<NumericRowReader> NUMERIC_ROW_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> row -> row.getInt(0);
+            case INT64 -> row -> row.getLong(0);
+            case FLOAT -> row -> row.getFloat(0);
+            case DOUBLE -> row -> row.getDouble(0);
+            default ->
+                row -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static final TypeService<IndexedNumericRowReader> INDEXED_NUMERIC_ROW_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> Row::getInt;
+            case INT64 -> Row::getLong;
+            case FLOAT -> Row::getFloat;
+            case DOUBLE -> Row::getDouble;
+            default ->
+                (row, index) -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static final TypeService<NumericRowWriter> NUMERIC_ROW_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (row, collector) -> collector.putInt(row.getTime(), row.getInt(0));
+            case INT64 -> (row, collector) -> collector.putLong(row.getTime(), row.getLong(0));
+            case FLOAT -> (row, collector) -> collector.putFloat(row.getTime(), row.getFloat(0));
+            case DOUBLE -> (row, collector) -> collector.putDouble(row.getTime(), row.getDouble(0));
+            default ->
+                (row, collector) -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static final TypeService<RowValueReader> ROW_VALUE_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> row -> row.getInt(0);
+            case INT64 -> row -> row.getLong(0);
+            case FLOAT -> row -> row.getFloat(0);
+            case DOUBLE -> row -> row.getDouble(0);
+            case BOOLEAN -> row -> row.getBoolean(0);
+            case TEXT -> row -> row.getString(0);
+            default -> row -> 0;
+          };
+
+  public static final TypeService<RowValueWriter> ROW_VALUE_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (collector, time, value) -> collector.putInt(time, (Integer) value);
+            case INT64 -> (collector, time, value) -> collector.putLong(time, (Long) value);
+            case FLOAT -> (collector, time, value) -> collector.putFloat(time, (Float) value);
+            case DOUBLE -> (collector, time, value) -> collector.putDouble(time, (Double) value);
+            case BOOLEAN -> (collector, time, value) -> collector.putBoolean(time, (Boolean) value);
+            default -> (collector, time, value) -> {};
+          };
+
+  public static final TypeService<ColumnNumericReader> COLUMN_NUMERIC_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> Column::getInt;
+            case INT64 -> Column::getLong;
+            case FLOAT -> Column::getFloat;
+            case DOUBLE -> Column::getDouble;
+            case BOOLEAN -> (column, index) -> column.getBoolean(index) ? 1.0D : 0.0D;
+            default ->
+                (column, index) -> {
+                  throw new IllegalArgumentException(
+                      "Unsupported data type: " + type.getTypeEnum());
+                };
+          };
+
+  private static final IndexedNumericRowReader[] INDEXED_NUMERIC_ROW_READERS =
+      new IndexedNumericRowReader[Type.values().length];
+  private static final RowValueReader[] ROW_VALUE_READERS =
+      new RowValueReader[Type.values().length];
+  private static final RowValueWriter[] ROW_VALUE_WRITERS =
+      new RowValueWriter[Type.values().length];
+
+  static {
+    for (Type udfType : Type.values()) {
+      org.apache.tsfile.read.common.type.Type readType = toReadType(udfType);
+      INDEXED_NUMERIC_ROW_READERS[udfType.ordinal()] =
+          INDEXED_NUMERIC_ROW_READER_SERVICE.call(readType);
+      ROW_VALUE_READERS[udfType.ordinal()] = ROW_VALUE_READER_SERVICE.call(readType);
+      ROW_VALUE_WRITERS[udfType.ordinal()] = ROW_VALUE_WRITER_SERVICE.call(readType);
+    }
+  }
+
+  public static IndexedNumericRowReader indexedNumericRowReader(Type type) {
+    return INDEXED_NUMERIC_ROW_READERS[type.ordinal()];
+  }
+
+  public static RowValueReader rowValueReader(Type type) {
+    return ROW_VALUE_READERS[type.ordinal()];
+  }
+
+  public static RowValueWriter rowValueWriter(Type type) {
+    return ROW_VALUE_WRITERS[type.ordinal()];
+  }
+
+  public static <T> TypeService<T> numericService(
+      T intService, T longService, T floatService, T doubleService, T unsupportedService) {
+    return type ->
+        switch (type.getTypeEnum()) {
+          case INT32 -> intService;
+          case INT64 -> longService;
+          case FLOAT -> floatService;
+          case DOUBLE -> doubleService;
+          default -> unsupportedService;
+        };
+  }
+
+  public static <T> TypeService<T> scalarService(
+      T booleanService,
+      T intService,
+      T longService,
+      T floatService,
+      T doubleService,
+      T textService,
+      T unsupportedService) {
+    return type ->
+        switch (type.getTypeEnum()) {
+          case BOOLEAN -> booleanService;
+          case INT32 -> intService;
+          case INT64 -> longService;
+          case FLOAT -> floatService;
+          case DOUBLE -> doubleService;
+          case TEXT, STRING -> textService;
+          default -> unsupportedService;
+        };
+  }
+
+  public static final TypeService<NumericWindowWriter> NUMERIC_WINDOW_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putInt(times[i], (int) Math.round(values[i]));
+                  }
+                };
+            case INT64 ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putLong(times[i], Math.round(values[i]));
+                  }
+                };
+            case FLOAT ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putFloat(times[i], (float) values[i]);
+                  }
+                };
+            case DOUBLE ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putDouble(times[i], values[i]);
+                  }
+                };
+            default ->
+                (times, values, collector) -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static final TypeService<NumericValueWriter> NUMERIC_VALUE_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (time, value, collector) -> collector.putInt(time, (int) value);
+            case INT64 -> (time, value, collector) -> collector.putLong(time, (long) value);
+            case FLOAT -> (time, value, collector) -> collector.putFloat(time, (float) value);
+            case DOUBLE -> (time, value, collector) -> collector.putDouble(time, value);
+            default ->
+                (time, value, collector) -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static final TypeService<NumericWindowWriter> NUMERIC_CAST_WINDOW_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putInt(times[i], (int) values[i]);
+                  }
+                };
+            case INT64 ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putLong(times[i], (long) values[i]);
+                  }
+                };
+            case FLOAT ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putFloat(times[i], (float) values[i]);
+                  }
+                };
+            case DOUBLE ->
+                (times, values, collector) -> {
+                  for (int i = 0; i < times.length; i++) {
+                    collector.putDouble(times[i], values[i]);
+                  }
+                };
+            default ->
+                (times, values, collector) -> {
+                  throw new NoNumberException();
+                };
+          };
+
+  public static org.apache.tsfile.read.common.type.Type toReadType(Type type) {
+    return org.apache.tsfile.read.common.type.Type.fromTsDataType(
+        TSDataType.getTsDataType(type.getType()));
+  }
+
+  private TypeServices() {}
+
+  @FunctionalInterface
+  public interface NumericRowReader {
+    double read(Row row) throws IOException, NoNumberException;
+  }
+
+  @FunctionalInterface
+  public interface IndexedNumericRowReader {
+    double read(Row row, int index) throws IOException, NoNumberException;
+  }
+
+  @FunctionalInterface
+  public interface NumericRowWriter {
+    void write(Row row, PointCollector collector) throws IOException, NoNumberException;
+  }
+
+  @FunctionalInterface
+  public interface RowValueReader {
+    Object read(Row row) throws IOException;
+  }
+
+  @FunctionalInterface
+  public interface RowValueWriter {
+    void write(PointCollector collector, long time, Object value) throws IOException;
+  }
+
+  @FunctionalInterface
+  public interface ColumnNumericReader {
+    double read(Column column, int index);
+  }
+
+  @FunctionalInterface
+  public interface NumericWindowWriter {
+    void write(long[] times, double[] values, PointCollector collector)
+        throws IOException, NoNumberException;
+  }
+
+  @FunctionalInterface
+  public interface NumericValueWriter {
+    void write(long time, double value, PointCollector collector)
+        throws IOException, NoNumberException;
+  }
+}
diff --git a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java
index c493445c19b..293e412e933 100644
--- a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java
+++ b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java
@@ -50,34 +50,11 @@ public class Util {
    * @throws NoNumberException when getting a no number datatype
    */
   public static double getValueAsDouble(Row row, int index) throws IOException, NoNumberException {
-    double ans = 0;
     try {
-      switch (row.getDataType(index)) {
-        case INT32:
-          ans = row.getInt(index);
-          break;
-        case INT64:
-          ans = row.getLong(index);
-          break;
-        case FLOAT:
-          ans = row.getFloat(index);
-          break;
-        case DOUBLE:
-          ans = row.getDouble(index);
-          break;
-        case DATE:
-        case BLOB:
-        case BOOLEAN:
-        case STRING:
-        case TEXT:
-        case TIMESTAMP:
-        default:
-          throw new NoNumberException();
-      }
+      return TypeServices.indexedNumericRowReader(row.getDataType(index)).read(row, index);
     } catch (IOException e) {
       throw new IOException(LibraryUdfMessages.FAIL_TO_GET_DATA_TYPE_IN_ROW + row.getTime(), e);
     }
-    return ans;
   }
 
   /**
@@ -98,34 +75,7 @@ public class Util {
    * @return value from 0th column from Row
    */
   public static Object getValueAsObject(Row row) throws IOException {
-    Object ans = 0;
-    switch (row.getDataType(0)) {
-      case INT32:
-        ans = row.getInt(0);
-        break;
-      case INT64:
-        ans = row.getLong(0);
-        break;
-      case FLOAT:
-        ans = row.getFloat(0);
-        break;
-      case DOUBLE:
-        ans = row.getDouble(0);
-        break;
-      case BOOLEAN:
-        ans = row.getBoolean(0);
-        break;
-      case TEXT:
... 46 lines suppressed ...


(iotdb) 10/14: multiple refactors

Posted by ji...@apache.org.
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 ade03e2d1fd93807431adc8f3029650e92d9e891
Author: Tian Jiang <jt...@163.com>
AuthorDate: Thu Aug 27 18:29:07 2026 +0800

    multiple refactors
---
 .../iotdb/commons/udf/builtin/TypeServices.java    | 206 +++++++++++++++++++++
 .../commons/udf/builtin/UDTFCommonDerivative.java  |  41 +---
 .../udf/builtin/UDTFCommonValueDifference.java     |  42 +----
 .../iotdb/commons/udf/builtin/UDTFDerivative.java  |  11 ++
 .../udf/builtin/UDTFNonNegativeDerivative.java     |  48 +----
 .../builtin/UDTFNonNegativeValueDifference.java    |  49 +----
 .../commons/udf/builtin/UDTFValueDifference.java   |  12 ++
 .../iotdb/commons/udf/builtin/UDTFValueTrend.java  |  42 ++---
 8 files changed, 259 insertions(+), 192 deletions(-)

diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
new file mode 100644
index 00000000000..f663171abae
--- /dev/null
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
@@ -0,0 +1,206 @@
+/*
+ * 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.commons.udf.builtin;
+
+import org.apache.iotdb.udf.api.access.Row;
+import org.apache.iotdb.udf.api.collector.PointCollector;
+import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
+
+import org.apache.tsfile.read.common.type.service.TypeService;
+
+import java.io.IOException;
+
+/** Type-specific operations used by numeric built-in UDFs. */
+final class TypeServices {
+
+  // Select the primitive Row accessor once per UDF instance instead of switching for every row.
+  static final TypeService<PreviousValueReader> VALUE_TREND_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (target, row) -> target.previousInt = row.getInt(0);
+            case INT64 -> (target, row) -> target.previousLong = row.getLong(0);
+            case FLOAT -> (target, row) -> target.previousFloat = row.getFloat(0);
+            case DOUBLE -> (target, row) -> target.previousDouble = row.getDouble(0);
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, row) -> {
+                  throw target.invalidDataType();
+                };
+          };
+
+  static final TypeService<ValueDifferenceOperator> VALUE_DIFFERENCE_OPERATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (target, time, row, collector) -> {
+                  int current = row.getInt(0);
+                  collector.putInt(time, current - target.previousInt);
+                  target.previousInt = current;
+                };
+            case INT64 ->
+                (target, time, row, collector) -> {
+                  long current = row.getLong(0);
+                  collector.putLong(time, current - target.previousLong);
+                  target.previousLong = current;
+                };
+            case FLOAT ->
+                (target, time, row, collector) -> {
+                  float current = row.getFloat(0);
+                  collector.putFloat(time, current - target.previousFloat);
+                  target.previousFloat = current;
+                };
+            case DOUBLE ->
+                (target, time, row, collector) -> {
+                  double current = row.getDouble(0);
+                  collector.putDouble(time, current - target.previousDouble);
+                  target.previousDouble = current;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, time, row, collector) -> {
+                  throw target.invalidDataType();
+                };
+          };
+
+  static final TypeService<ValueDifferenceOperator> NON_NEGATIVE_VALUE_DIFFERENCE_OPERATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (target, time, row, collector) -> {
+                  int current = row.getInt(0);
+                  collector.putInt(time, Math.abs(current - target.previousInt));
+                  target.previousInt = current;
+                };
+            case INT64 ->
+                (target, time, row, collector) -> {
+                  long current = row.getLong(0);
+                  collector.putLong(time, Math.abs(current - target.previousLong));
+                  target.previousLong = current;
+                };
+            case FLOAT ->
+                (target, time, row, collector) -> {
+                  float current = row.getFloat(0);
+                  collector.putFloat(time, Math.abs(current - target.previousFloat));
+                  target.previousFloat = current;
+                };
+            case DOUBLE ->
+                (target, time, row, collector) -> {
+                  double current = row.getDouble(0);
+                  collector.putDouble(time, Math.abs(current - target.previousDouble));
+                  target.previousDouble = current;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, time, row, collector) -> {
+                  throw target.invalidDataType();
+                };
+          };
+
+  static final TypeService<DerivativeOperator> DERIVATIVE_OPERATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (target, time, row, collector, timeDelta) -> {
+                  int current = row.getInt(0);
+                  collector.putDouble(time, (current - target.previousInt) / timeDelta);
+                  target.previousInt = current;
+                };
+            case INT64 ->
+                (target, time, row, collector, timeDelta) -> {
+                  long current = row.getLong(0);
+                  collector.putDouble(time, (current - target.previousLong) / timeDelta);
+                  target.previousLong = current;
+                };
+            case FLOAT ->
+                (target, time, row, collector, timeDelta) -> {
+                  float current = row.getFloat(0);
+                  collector.putDouble(time, (current - target.previousFloat) / timeDelta);
+                  target.previousFloat = current;
+                };
+            case DOUBLE ->
+                (target, time, row, collector, timeDelta) -> {
+                  double current = row.getDouble(0);
+                  collector.putDouble(time, (current - target.previousDouble) / timeDelta);
+                  target.previousDouble = current;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, time, row, collector, timeDelta) -> {
+                  throw target.invalidDataType();
+                };
+          };
+
+  static final TypeService<DerivativeOperator> NON_NEGATIVE_DERIVATIVE_OPERATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (target, time, row, collector, timeDelta) -> {
+                  int current = row.getInt(0);
+                  collector.putDouble(time, Math.abs(current - target.previousInt) / timeDelta);
+                  target.previousInt = current;
+                };
+            case INT64 ->
+                (target, time, row, collector, timeDelta) -> {
+                  long current = row.getLong(0);
+                  collector.putDouble(time, Math.abs(current - target.previousLong) / timeDelta);
+                  target.previousLong = current;
+                };
+            case FLOAT ->
+                (target, time, row, collector, timeDelta) -> {
+                  float current = row.getFloat(0);
+                  collector.putDouble(time, Math.abs(current - target.previousFloat) / timeDelta);
+                  target.previousFloat = current;
+                };
+            case DOUBLE ->
+                (target, time, row, collector, timeDelta) -> {
+                  double current = row.getDouble(0);
+                  collector.putDouble(time, Math.abs(current - target.previousDouble) / timeDelta);
+                  target.previousDouble = current;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, time, row, collector, timeDelta) -> {
+                  throw target.invalidDataType();
+                };
+          };
+
+  static {
+    VALUE_TREND_READER_SERVICE.check();
+    VALUE_DIFFERENCE_OPERATOR_SERVICE.check();
+    NON_NEGATIVE_VALUE_DIFFERENCE_OPERATOR_SERVICE.check();
+    DERIVATIVE_OPERATOR_SERVICE.check();
+    NON_NEGATIVE_DERIVATIVE_OPERATOR_SERVICE.check();
+  }
+
+  private TypeServices() {}
+
+  @FunctionalInterface
+  interface PreviousValueReader {
+    void read(UDTFValueTrend target, Row row)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface ValueDifferenceOperator {
+    void apply(UDTFValueTrend target, long time, Row row, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface DerivativeOperator {
+    void apply(
+        UDTFValueTrend target, long time, Row row, PointCollector collector, double timeDelta)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+}
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java
index cb5cbcc0486..0a80ad2de28 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java
@@ -19,11 +19,9 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
 
 import java.io.IOException;
 
@@ -34,44 +32,7 @@ public class UDTFCommonDerivative extends UDTFDerivative {
       throws UDFInputSeriesDataTypeNotValidException, IOException {
     long currentTime = row.getTime();
     double timeDelta = (double) currentTime - previousTime;
-    switch (dataType) {
-      case INT32:
-        int currentInt = row.getInt(0);
-        collector.putDouble(currentTime, (currentInt - previousInt) / timeDelta);
-        previousInt = currentInt;
-        break;
-      case INT64:
-        long currentLong = row.getLong(0);
-        collector.putDouble(currentTime, (currentLong - previousLong) / timeDelta);
-        previousLong = currentLong;
-        break;
-      case FLOAT:
-        float currentFloat = row.getFloat(0);
-        collector.putDouble(currentTime, (currentFloat - previousFloat) / timeDelta);
-        previousFloat = currentFloat;
-        break;
-      case DOUBLE:
-        double currentDouble = row.getDouble(0);
-        collector.putDouble(currentTime, (currentDouble - previousDouble) / timeDelta);
-        previousDouble = currentDouble;
-        break;
-      case DATE:
-      case BOOLEAN:
-      case TIMESTAMP:
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    derivativeOperator.apply(this, currentTime, row, collector, timeDelta);
     previousTime = currentTime;
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java
index 7f8d81f6b82..0a4deac2024 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java
@@ -19,11 +19,9 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
 
 import java.io.IOException;
 
@@ -32,44 +30,6 @@ public class UDTFCommonValueDifference extends UDTFValueDifference {
   @Override
   protected void doTransform(Row row, PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        int currentInt = row.getInt(0);
-        collector.putInt(time, currentInt - previousInt);
-        previousInt = currentInt;
-        break;
-      case INT64:
-        long currentLong = row.getLong(0);
-        collector.putLong(time, currentLong - previousLong);
-        previousLong = currentLong;
-        break;
-      case FLOAT:
-        float currentFloat = row.getFloat(0);
-        collector.putFloat(time, currentFloat - previousFloat);
-        previousFloat = currentFloat;
-        break;
-      case DOUBLE:
-        double currentDouble = row.getDouble(0);
-        collector.putDouble(time, currentDouble - previousDouble);
-        previousDouble = currentDouble;
-        break;
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case TIMESTAMP:
-      case TEXT:
-      case BOOLEAN:
-      case DATE:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    valueDifferenceOperator.apply(this, row.getTime(), row, collector);
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java
index 81645d47787..2457ed89811 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java
@@ -29,16 +29,27 @@ import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
+import org.apache.tsfile.read.common.type.service.TypeService;
+
 import java.io.IOException;
 
 public abstract class UDTFDerivative extends UDTFValueTrend {
 
   protected long previousTime;
+  protected TypeServices.DerivativeOperator derivativeOperator;
+
+  protected TypeService<TypeServices.DerivativeOperator> derivativeOperatorService() {
+    return TypeServices.DERIVATIVE_OPERATOR_SERVICE;
+  }
 
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
     dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    org.apache.tsfile.read.common.type.Type type =
+        org.apache.tsfile.read.common.type.Type.fromTsDataType(dataType);
+    previousValueReader = TypeServices.VALUE_TREND_READER_SERVICE.call(type);
+    derivativeOperator = derivativeOperatorService().call(type);
     configurations.setAccessStrategy(new RowByRowAccessStrategy()).setOutputDataType(Type.DOUBLE);
   }
 
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java
index 077db957f6e..3847e8638b4 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java
@@ -19,59 +19,27 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.read.common.type.service.TypeService;
 
 import java.io.IOException;
 
 public class UDTFNonNegativeDerivative extends UDTFDerivative {
 
+  @Override
+  protected TypeService<TypeServices.DerivativeOperator> derivativeOperatorService() {
+    return TypeServices.NON_NEGATIVE_DERIVATIVE_OPERATOR_SERVICE;
+  }
+
   @Override
   protected void doTransform(Row row, PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
     long currentTime = row.getTime();
     double timeDelta = (double) currentTime - previousTime;
-    switch (dataType) {
-      case INT32:
-        int currentInt = row.getInt(0);
-        collector.putDouble(currentTime, Math.abs(currentInt - previousInt) / timeDelta);
-        previousInt = currentInt;
-        break;
-      case INT64:
-        long currentLong = row.getLong(0);
-        collector.putDouble(currentTime, Math.abs(currentLong - previousLong) / timeDelta);
-        previousLong = currentLong;
-        break;
-      case FLOAT:
-        float currentFloat = row.getFloat(0);
-        collector.putDouble(currentTime, Math.abs(currentFloat - previousFloat) / timeDelta);
-        previousFloat = currentFloat;
-        break;
-      case DOUBLE:
-        double currentDouble = row.getDouble(0);
-        collector.putDouble(currentTime, Math.abs(currentDouble - previousDouble) / timeDelta);
-        previousDouble = currentDouble;
-        break;
-      case DATE:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case TIMESTAMP:
-      case BOOLEAN:
-      case TEXT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    derivativeOperator.apply(this, currentTime, row, collector, timeDelta);
     previousTime = currentTime;
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java
index fcb68817f15..2cafcf446c3 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java
@@ -19,57 +19,24 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.read.common.type.service.TypeService;
 
 import java.io.IOException;
 
 public class UDTFNonNegativeValueDifference extends UDTFValueDifference {
 
+  @Override
+  protected TypeService<TypeServices.ValueDifferenceOperator> valueDifferenceOperatorService() {
+    return TypeServices.NON_NEGATIVE_VALUE_DIFFERENCE_OPERATOR_SERVICE;
+  }
+
   @Override
   protected void doTransform(Row row, PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        int currentInt = row.getInt(0);
-        collector.putInt(time, Math.abs(currentInt - previousInt));
-        previousInt = currentInt;
-        break;
-      case INT64:
-        long currentLong = row.getLong(0);
-        collector.putLong(time, Math.abs(currentLong - previousLong));
-        previousLong = currentLong;
-        break;
-      case FLOAT:
-        float currentFloat = row.getFloat(0);
-        collector.putFloat(time, Math.abs(currentFloat - previousFloat));
-        previousFloat = currentFloat;
-        break;
-      case DOUBLE:
-        double currentDouble = row.getDouble(0);
-        collector.putDouble(time, Math.abs(currentDouble - previousDouble));
-        previousDouble = currentDouble;
-        break;
-      case BOOLEAN:
-      case TEXT:
-      case STRING:
-      case TIMESTAMP:
-      case BLOB:
-      case OBJECT:
-      case DATE:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    valueDifferenceOperator.apply(this, row.getTime(), row, collector);
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java
index 9804e1ce374..81d8cc78aa7 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java
@@ -28,14 +28,26 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 
+import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.read.common.type.service.TypeService;
+
 import java.io.IOException;
 
 public abstract class UDTFValueDifference extends UDTFValueTrend {
 
+  protected TypeServices.ValueDifferenceOperator valueDifferenceOperator;
+
+  protected TypeService<TypeServices.ValueDifferenceOperator> valueDifferenceOperatorService() {
+    return TypeServices.VALUE_DIFFERENCE_OPERATOR_SERVICE;
+  }
+
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
     dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    Type type = Type.fromTsDataType(dataType);
+    previousValueReader = TypeServices.VALUE_TREND_READER_SERVICE.call(type);
+    valueDifferenceOperator = valueDifferenceOperatorService().call(type);
     configurations
         .setAccessStrategy(new RowByRowAccessStrategy())
         .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java
index d4487fdfd95..1f8a7718822 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java
@@ -42,6 +42,7 @@ public abstract class UDTFValueTrend implements UDTF {
   protected double previousDouble = 0;
 
   protected TSDataType dataType;
+  protected TypeServices.PreviousValueReader previousValueReader;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -52,36 +53,17 @@ public abstract class UDTFValueTrend implements UDTF {
 
   protected void updatePreviousValue(Row row)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    switch (dataType) {
-      case INT32:
-        previousInt = row.getInt(0);
-        break;
-      case INT64:
-        previousLong = row.getLong(0);
-        break;
-      case FLOAT:
-        previousFloat = row.getFloat(0);
-        break;
-      case DOUBLE:
-        previousDouble = row.getDouble(0);
-        break;
-      case TEXT:
-      case BOOLEAN:
-      case TIMESTAMP:
-      case STRING:
-      case DATE:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    previousValueReader.read(this, row);
+  }
+
+  protected UDFInputSeriesDataTypeNotValidException invalidDataType() {
+    return new UDFInputSeriesDataTypeNotValidException(
+        0,
+        UDFDataTypeTransformer.transformToUDFDataType(dataType),
+        Type.INT32,
+        Type.INT64,
+        Type.FLOAT,
+        Type.DOUBLE);
   }
 
   protected abstract void doTransform(Row row, PointCollector collector)


(iotdb) 04/14: multiple refactors

Posted by ji...@apache.org.
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 e1f2247535dfa324f2f4ade3235b8e5b1d21b018
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 11:34:46 2026 +0800

    multiple refactors
---
 .../AbstractGreatestLeastColumnTransformer.java    | 46 +-------------
 .../multi/BinaryGreatestColumnTransformer.java     |  2 +-
 .../column/multi/BinaryLeastColumnTransformer.java |  2 +-
 .../multi/BooleanGreatestColumnTransformer.java    |  2 +-
 .../multi/BooleanLeastColumnTransformer.java       |  2 +-
 .../multi/DoubleGreatestColumnTransformer.java     |  2 +-
 .../column/multi/DoubleLeastColumnTransformer.java |  2 +-
 .../multi/FloatGreatestColumnTransformer.java      |  2 +-
 .../column/multi/FloatLeastColumnTransformer.java  |  2 +-
 .../multi/Int32GreatestColumnTransformer.java      |  2 +-
 .../column/multi/Int32LeastColumnTransformer.java  |  2 +-
 .../multi/Int64GreatestColumnTransformer.java      |  2 +-
 .../column/multi/Int64LeastColumnTransformer.java  |  2 +-
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 74 ++++++++++++++++++++++
 .../apache/iotdb/calc/utils/TypeServicesTest.java  | 22 +++++++
 15 files changed, 111 insertions(+), 55 deletions(-)

diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java
index 434f371d0d3..2a6f3e90934 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java
@@ -20,11 +20,11 @@
 package org.apache.iotdb.calc.transformation.dag.column.multi;
 
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
+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.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 
 import java.util.List;
 
@@ -59,52 +59,12 @@ public abstract class AbstractGreatestLeastColumnTransformer extends MultiColumn
 
   public static ColumnTransformer getGreatestColumnTransformer(
       Type type, List<ColumnTransformer> columnTransformers) {
-    TypeEnum typeEnum = type.getTypeEnum();
-    switch (typeEnum) {
-      case BOOLEAN:
-        return new BooleanGreatestColumnTransformer(type, columnTransformers);
-      case INT32:
-      case DATE:
-        return new Int32GreatestColumnTransformer(type, columnTransformers);
-      case INT64:
-      case TIMESTAMP:
-        return new Int64GreatestColumnTransformer(type, columnTransformers);
-      case FLOAT:
-        return new FloatGreatestColumnTransformer(type, columnTransformers);
-      case DOUBLE:
-        return new DoubleGreatestColumnTransformer(type, columnTransformers);
-      case STRING:
-      case TEXT:
-        return new BinaryGreatestColumnTransformer(type, columnTransformers);
-      default:
-        throw new UnsupportedOperationException(
-            String.format("Unsupported data type: %s", typeEnum));
-    }
+    return TypeServices.GREATEST_COLUMN_TRANSFORMER_SERVICE.call(type).apply(columnTransformers);
   }
 
   public static ColumnTransformer getLeastColumnTransformer(
       Type type, List<ColumnTransformer> columnTransformers) {
-    TypeEnum typeEnum = type.getTypeEnum();
-    switch (typeEnum) {
-      case BOOLEAN:
-        return new BooleanLeastColumnTransformer(type, columnTransformers);
-      case INT32:
-      case DATE:
-        return new Int32LeastColumnTransformer(type, columnTransformers);
-      case INT64:
-      case TIMESTAMP:
-        return new Int64LeastColumnTransformer(type, columnTransformers);
-      case FLOAT:
-        return new FloatLeastColumnTransformer(type, columnTransformers);
-      case DOUBLE:
-        return new DoubleLeastColumnTransformer(type, columnTransformers);
-      case STRING:
-      case TEXT:
-        return new BinaryLeastColumnTransformer(type, columnTransformers);
-      default:
-        throw new UnsupportedOperationException(
-            String.format("Unsupported data type: %s", typeEnum));
-    }
+    return TypeServices.LEAST_COLUMN_TRANSFORMER_SERVICE.call(type).apply(columnTransformers);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryGreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryGreatestColumnTransformer.java
index ae15ca4ee92..45602660afa 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryGreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryGreatestColumnTransformer.java
@@ -29,7 +29,7 @@ import org.apache.tsfile.utils.Binary;
 import java.util.List;
 
 public class BinaryGreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected BinaryGreatestColumnTransformer(
+  public BinaryGreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryLeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryLeastColumnTransformer.java
index 06c43b134d0..2370f258dd4 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryLeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BinaryLeastColumnTransformer.java
@@ -29,7 +29,7 @@ import org.apache.tsfile.utils.Binary;
 import java.util.List;
 
 public class BinaryLeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected BinaryLeastColumnTransformer(
+  public BinaryLeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanGreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanGreatestColumnTransformer.java
index 89ada058444..da4320b9277 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanGreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanGreatestColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class BooleanGreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected BooleanGreatestColumnTransformer(
+  public BooleanGreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanLeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanLeastColumnTransformer.java
index 88054e7cf75..48d57c038c5 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanLeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/BooleanLeastColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class BooleanLeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected BooleanLeastColumnTransformer(
+  public BooleanLeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleGreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleGreatestColumnTransformer.java
index cf76de49088..de2711e9d80 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleGreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleGreatestColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class DoubleGreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected DoubleGreatestColumnTransformer(
+  public DoubleGreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleLeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleLeastColumnTransformer.java
index c98f124b292..e5da4489e62 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleLeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/DoubleLeastColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class DoubleLeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected DoubleLeastColumnTransformer(
+  public DoubleLeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatGreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatGreatestColumnTransformer.java
index ece454c9eb5..87758e75351 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatGreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatGreatestColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class FloatGreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected FloatGreatestColumnTransformer(
+  public FloatGreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatLeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatLeastColumnTransformer.java
index 5d7c6155dff..f06e8c69cb9 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatLeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/FloatLeastColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class FloatLeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected FloatLeastColumnTransformer(
+  public FloatLeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32GreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32GreatestColumnTransformer.java
index ed25278944f..91df173129d 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32GreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32GreatestColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class Int32GreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected Int32GreatestColumnTransformer(
+  public Int32GreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32LeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32LeastColumnTransformer.java
index b33b0022b10..97fc995812d 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32LeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int32LeastColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class Int32LeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected Int32LeastColumnTransformer(
+  public Int32LeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64GreatestColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64GreatestColumnTransformer.java
index 8f8edb7ab13..0109ef81f45 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64GreatestColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64GreatestColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class Int64GreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected Int64GreatestColumnTransformer(
+  public Int64GreatestColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64LeastColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64LeastColumnTransformer.java
index 5c26f6ae92b..b579b73f410 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64LeastColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/multi/Int64LeastColumnTransformer.java
@@ -28,7 +28,7 @@ import org.apache.tsfile.read.common.type.Type;
 import java.util.List;
 
 public class Int64LeastColumnTransformer extends AbstractGreatestLeastColumnTransformer {
-  protected Int64LeastColumnTransformer(
+  public Int64LeastColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList) {
     super(returnType, columnTransformerList);
   }
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 8377ce7518d..5980317b17c 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
@@ -68,6 +68,19 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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.transformation.dag.column.ColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.BinaryGreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.BinaryLeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.BooleanGreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.BooleanLeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.DoubleGreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.DoubleLeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.FloatGreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.FloatLeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.Int32GreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.Int32LeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.Int64GreatestColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.Int64LeastColumnTransformer;
 import org.apache.iotdb.calc.utils.datastructure.SortKey;
 
 import org.apache.tsfile.block.column.Column;
@@ -82,6 +95,7 @@ import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.util.Comparator;
+import java.util.List;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.function.IntUnaryOperator;
@@ -272,6 +286,59 @@ public class TypeServices {
                     throw new UnSupportedDataTypeException(type.toString()).setChecked(true);
               };
 
+  public static final TypeService<Function<List<ColumnTransformer>, ColumnTransformer>>
+      GREATEST_COLUMN_TRANSFORMER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case BOOLEAN ->
+                    columnTransformers ->
+                        new BooleanGreatestColumnTransformer(type, columnTransformers);
+                case INT32, DATE ->
+                    columnTransformers ->
+                        new Int32GreatestColumnTransformer(type, columnTransformers);
+                case INT64, TIMESTAMP ->
+                    columnTransformers ->
+                        new Int64GreatestColumnTransformer(type, columnTransformers);
+                case FLOAT ->
+                    columnTransformers ->
+                        new FloatGreatestColumnTransformer(type, columnTransformers);
+                case DOUBLE ->
+                    columnTransformers ->
+                        new DoubleGreatestColumnTransformer(type, columnTransformers);
+                case STRING, TEXT ->
+                    columnTransformers ->
+                        new BinaryGreatestColumnTransformer(type, columnTransformers);
+                case BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    columnTransformers -> {
+                      throw unsupportedGreatestLeastDataType(type);
+                    };
+              };
+
+  public static final TypeService<Function<List<ColumnTransformer>, ColumnTransformer>>
+      LEAST_COLUMN_TRANSFORMER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case BOOLEAN ->
+                    columnTransformers ->
+                        new BooleanLeastColumnTransformer(type, columnTransformers);
+                case INT32, DATE ->
+                    columnTransformers -> new Int32LeastColumnTransformer(type, columnTransformers);
+                case INT64, TIMESTAMP ->
+                    columnTransformers -> new Int64LeastColumnTransformer(type, columnTransformers);
+                case FLOAT ->
+                    columnTransformers -> new FloatLeastColumnTransformer(type, columnTransformers);
+                case DOUBLE ->
+                    columnTransformers ->
+                        new DoubleLeastColumnTransformer(type, columnTransformers);
+                case STRING, TEXT ->
+                    columnTransformers ->
+                        new BinaryLeastColumnTransformer(type, columnTransformers);
+                case BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    columnTransformers -> {
+                      throw unsupportedGreatestLeastDataType(type);
+                    };
+              };
+
   public static final TypeService<Function<TsPrimitiveType, Object>>
       PRIMITIVE_TYPE_VALUE_EXTRACTOR_SERVICE =
           type ->
@@ -510,6 +577,8 @@ public class TypeServices {
     PREVIOUS_FILL_SERVICE.check();
     NEXT_FILL_SERVICE.check();
     JOIN_KEY_COMPARATOR_SERVICE.check();
+    GREATEST_COLUMN_TRANSFORMER_SERVICE.check();
+    LEAST_COLUMN_TRANSFORMER_SERVICE.check();
     MERGE_SORT_COMPARATOR_SERVICE.check();
     MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE.check();
     MEMORY_USAGE_OF_ONE_SERIALIZABLE_ROW_FIELD_SERVICE.check();
@@ -530,6 +599,11 @@ public class TypeServices {
     return new IllegalArgumentException(CalcMessages.UNKNOWN_DATATYPE + type.getTypeEnum());
   }
 
+  private static UnsupportedOperationException unsupportedGreatestLeastDataType(final Type type) {
+    return new UnsupportedOperationException(
+        CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+  }
+
   public interface DefaultEncodingProvider {
     TSEncoding getDefaultBooleanEncoding();
 
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
index 6f5ac8a9b01..39ca2a1a2b4 100644
--- 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
@@ -23,6 +23,7 @@ import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.Jo
 import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparatorFactory;
 import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition;
 import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
+import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.enums.TSDataType;
@@ -37,6 +38,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Optional;
 
 import static org.junit.Assert.assertEquals;
@@ -129,6 +131,26 @@ public class TypeServicesTest {
         () -> JoinKeyComparatorFactory.getComparator(Type.fromTsDataType(TSDataType.VECTOR), true));
   }
 
+  @Test
+  public void testGreatestLeastTransformerServicesUseTypeStrategies() {
+    List<ColumnTransformer> columnTransformers = Collections.emptyList();
+    assertNotNull(
+        TypeServices.GREATEST_COLUMN_TRANSFORMER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.INT32))
+            .apply(columnTransformers));
+    assertNotNull(
+        TypeServices.LEAST_COLUMN_TRANSFORMER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.DOUBLE))
+            .apply(columnTransformers));
+
+    Assert.assertThrows(
+        UnsupportedOperationException.class,
+        () ->
+            TypeServices.GREATEST_COLUMN_TRANSFORMER_SERVICE
+                .call(Type.fromTsDataType(TSDataType.VECTOR))
+                .apply(columnTransformers));
+  }
+
   // Covers every supported RANGE-frame type and guards native integer overflow and long precision.
   @Test
   public void testRangeFrameComparatorPreservesNativeArithmetic() {


(iotdb) 12/14: multiple refactors

Posted by ji...@apache.org.
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 49a2e7a1d12a5430c6c6c98683e28ccac8582f2f
Author: Tian Jiang <jt...@163.com>
AuthorDate: Fri Aug 28 11:57:33 2026 +0800

    multiple refactors
---
 .../iotdb/commons/udf/builtin/TypeServices.java    | 414 +++++++++++++++++++++
 .../apache/iotdb/commons/udf/builtin/UDTFAbs.java  | 106 +-----
 .../iotdb/commons/udf/builtin/UDTFBottomK.java     | 105 +++---
 .../commons/udf/builtin/UDTFChangePoints.java      | 178 ++++-----
 .../iotdb/commons/udf/builtin/UDTFConst.java       | 267 +++++--------
 .../udf/builtin/UDTFContinuouslySatisfy.java       |  79 +---
 .../udf/builtin/UDTFEqualSizeBucketAggSample.java  |  53 ++-
 .../udf/builtin/UDTFEqualSizeBucketM4Sample.java   |  39 +-
 .../builtin/UDTFEqualSizeBucketOutlierSample.java  |  53 ++-
 .../builtin/UDTFEqualSizeBucketRandomSample.java   |  40 +-
 .../apache/iotdb/commons/udf/builtin/UDTFM4.java   |  36 +-
 .../iotdb/commons/udf/builtin/UDTFSelectK.java     | 136 +++----
 .../apache/iotdb/commons/udf/builtin/UDTFTopK.java |  65 ++--
 13 files changed, 791 insertions(+), 780 deletions(-)

diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
index 2957e953ea2..b4c8333a3fe 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
@@ -20,11 +20,14 @@ package org.apache.iotdb.commons.udf.builtin;
 
 import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
+import org.apache.iotdb.udf.api.access.RowWindow;
 import org.apache.iotdb.udf.api.collector.PointCollector;
+import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.service.TypeService;
 
 import java.io.IOException;
@@ -203,6 +206,265 @@ final class TypeServices {
                 };
           };
 
+  static final TypeService<NumericRowCollector> NUMERIC_ROW_COLLECTOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> (row, collector) -> collector.putInt(row.getTime(), row.getInt(0));
+            case INT64 -> (row, collector) -> collector.putLong(row.getTime(), row.getLong(0));
+            case FLOAT -> (row, collector) -> collector.putFloat(row.getTime(), row.getFloat(0));
+            case DOUBLE -> (row, collector) -> collector.putDouble(row.getTime(), row.getDouble(0));
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (row, collector) -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
+  static final TypeService<AbsRowCollector> ABS_ROW_COLLECTOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (row, collector) -> collector.putInt(row.getTime(), Math.abs(row.getInt(0)));
+            case INT64 ->
+                (row, collector) -> collector.putLong(row.getTime(), Math.abs(row.getLong(0)));
+            case FLOAT ->
+                (row, collector) -> collector.putFloat(row.getTime(), Math.abs(row.getFloat(0)));
+            case DOUBLE ->
+                (row, collector) -> collector.putDouble(row.getTime(), Math.abs(row.getDouble(0)));
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (row, collector) -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
+  static final TypeService<AbsRowMapper> ABS_ROW_MAPPER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> row -> Math.abs(row.getInt(0));
+            case INT64 -> row -> Math.abs(row.getLong(0));
+            case FLOAT -> row -> Math.abs(row.getFloat(0));
+            case DOUBLE -> row -> Math.abs(row.getDouble(0));
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                row -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
+  static final TypeService<AbsColumnTransformer> ABS_COLUMN_TRANSFORMER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> UDTFAbs::transformInt;
+            case INT64 -> UDTFAbs::transformLong;
+            case FLOAT -> UDTFAbs::transformFloat;
+            case DOUBLE -> UDTFAbs::transformDouble;
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, columns, builder) -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
+  static final TypeService<NumericWindowTransformer<UDTFM4>> M4_WINDOW_TRANSFORMER_SERVICE =
+      numericWindowTransformerService(
+          UDTFM4::transformInt,
+          UDTFM4::transformLong,
+          UDTFM4::transformFloat,
+          UDTFM4::transformDouble);
+
+  static final TypeService<NumericWindowTransformer<UDTFEqualSizeBucketM4Sample>>
+      BUCKET_M4_WINDOW_TRANSFORMER_SERVICE =
+          numericWindowTransformerService(
+              UDTFEqualSizeBucketM4Sample::transformInt,
+              UDTFEqualSizeBucketM4Sample::transformLong,
+              UDTFEqualSizeBucketM4Sample::transformFloat,
+              UDTFEqualSizeBucketM4Sample::transformDouble);
+
+  static final TypeService<NumericWindowTransformer<UDTFEqualSizeBucketAggSample>>
+      BUCKET_AGG_WINDOW_TRANSFORMER_SERVICE =
+          numericWindowTransformerService(
+              UDTFEqualSizeBucketAggSample::aggregateInt,
+              UDTFEqualSizeBucketAggSample::aggregateLong,
+              UDTFEqualSizeBucketAggSample::aggregateFloat,
+              UDTFEqualSizeBucketAggSample::aggregateDouble);
+
+  static final TypeService<NumericWindowTransformer<UDTFEqualSizeBucketOutlierSample>>
+      BUCKET_OUTLIER_WINDOW_TRANSFORMER_SERVICE =
+          numericWindowTransformerService(
+              UDTFEqualSizeBucketOutlierSample::outlierSampleInt,
+              UDTFEqualSizeBucketOutlierSample::outlierSampleLong,
+              UDTFEqualSizeBucketOutlierSample::outlierSampleFloat,
+              UDTFEqualSizeBucketOutlierSample::outlierSampleDouble);
+
+  static final TypeService<ChangePointProcessor> CHANGE_POINT_PROCESSOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> UDTFChangePoints::transformBoolean;
+            case INT32 -> UDTFChangePoints::transformInt;
+            case INT64 -> UDTFChangePoints::transformLong;
+            case FLOAT -> UDTFChangePoints::transformFloat;
+            case DOUBLE -> UDTFChangePoints::transformDouble;
+            case TEXT -> UDTFChangePoints::transformString;
+            case DATE, TIMESTAMP, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, row, collector) -> {};
+          };
+
+  static final TypeService<SelectKRowTransformer> SELECT_K_ROW_TRANSFORMER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> (target, row) -> target.transformInt(row.getTime(), row.getInt(0));
+            case INT64, TIMESTAMP ->
+                (target, row) -> target.transformLong(row.getTime(), row.getLong(0));
+            case FLOAT -> (target, row) -> target.transformFloat(row.getTime(), row.getFloat(0));
+            case DOUBLE -> (target, row) -> target.transformDouble(row.getTime(), row.getDouble(0));
+            case TEXT, STRING ->
+                (target, row) -> target.transformString(row.getTime(), row.getString(0));
+            case BOOLEAN, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, row) -> {
+                  throw invalidSelectKDataType(type);
+                };
+          };
+
+  static final TypeService<SelectKTerminator> SELECT_K_TERMINATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> UDTFSelectK::terminateInt;
+            case INT64, TIMESTAMP -> UDTFSelectK::terminateLong;
+            case FLOAT -> UDTFSelectK::terminateFloat;
+            case DOUBLE -> UDTFSelectK::terminateDouble;
+            case TEXT, STRING -> UDTFSelectK::terminateString;
+            case BOOLEAN, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, collector) -> {
+                  throw invalidSelectKDataType(type);
+                };
+          };
+
+  static final TypeService<TopKQueueConstructor> TOP_K_QUEUE_CONSTRUCTOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> UDTFTopK::initializeIntQueue;
+            case INT64, TIMESTAMP -> UDTFTopK::initializeLongQueue;
+            case FLOAT -> UDTFTopK::initializeFloatQueue;
+            case DOUBLE -> UDTFTopK::initializeDoubleQueue;
+            case TEXT, STRING -> UDTFTopK::initializeStringQueue;
+            case BOOLEAN, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                target -> {
+                  throw invalidSelectKDataType(type);
+                };
+          };
+
+  static final TypeService<BottomKQueueConstructor> BOTTOM_K_QUEUE_CONSTRUCTOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> UDTFBottomK::initializeIntQueue;
+            case INT64, TIMESTAMP -> UDTFBottomK::initializeLongQueue;
+            case FLOAT -> UDTFBottomK::initializeFloatQueue;
+            case DOUBLE -> UDTFBottomK::initializeDoubleQueue;
+            case TEXT, STRING -> UDTFBottomK::initializeStringQueue;
+            case BOOLEAN, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                target -> {
+                  throw invalidSelectKDataType(type);
+                };
+          };
+
+  static final TypeService<ConstantParser> CONSTANT_PARSER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> UDTFConst::parseInt;
+            case DATE -> UDTFConst::parseDate;
+            case INT64, TIMESTAMP -> UDTFConst::parseLong;
+            case FLOAT -> UDTFConst::parseFloat;
+            case DOUBLE -> UDTFConst::parseDouble;
+            case BOOLEAN -> UDTFConst::parseBoolean;
+            case TEXT, STRING -> UDTFConst::parseText;
+            case BLOB, OBJECT -> UDTFConst::parseBlob;
+            case ROW, UNKNOWN, VECTOR ->
+                (target, parameters) -> {
+                  throw new UnsupportedOperationException();
+                };
+          };
+
+  static final TypeService<ConstantRowCollector> CONSTANT_ROW_COLLECTOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (target, row, collector) -> collector.putInt(row.getTime(), target.intValue());
+            case INT64, TIMESTAMP ->
+                (target, row, collector) -> collector.putLong(row.getTime(), target.longValue());
+            case FLOAT ->
+                (target, row, collector) -> collector.putFloat(row.getTime(), target.floatValue());
+            case DOUBLE ->
+                (target, row, collector) ->
+                    collector.putDouble(row.getTime(), target.doubleValue());
+            case BOOLEAN ->
+                (target, row, collector) ->
+                    collector.putBoolean(row.getTime(), target.booleanValue());
+            case TEXT, STRING, BLOB, OBJECT ->
+                (target, row, collector) ->
+                    collector.putBinary(row.getTime(), target.binaryValue());
+            case ROW, UNKNOWN, VECTOR ->
+                (target, row, collector) -> {
+                  throw new UnsupportedOperationException();
+                };
+          };
+
+  static final TypeService<ConstantRowMapper> CONSTANT_ROW_MAPPER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> UDTFConst::intValue;
+            case INT64, TIMESTAMP -> UDTFConst::longValue;
+            case FLOAT -> UDTFConst::floatValue;
+            case DOUBLE -> UDTFConst::doubleValue;
+            case BOOLEAN -> UDTFConst::booleanValue;
+            case TEXT, STRING, BLOB, OBJECT -> UDTFConst::binaryValue;
+            case ROW, UNKNOWN, VECTOR ->
+                target -> {
+                  throw new UnsupportedOperationException();
+                };
+          };
+
+  static final TypeService<ConstantColumnValueWriter> CONSTANT_COLUMN_VALUE_WRITER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> (target, builder) -> builder.writeInt(target.intValue());
+            case INT64, TIMESTAMP -> (target, builder) -> builder.writeLong(target.longValue());
+            case FLOAT -> (target, builder) -> builder.writeFloat(target.floatValue());
+            case DOUBLE -> (target, builder) -> builder.writeDouble(target.doubleValue());
+            case BOOLEAN -> (target, builder) -> builder.writeBoolean(target.booleanValue());
+            case TEXT, STRING, BLOB, OBJECT ->
+                (target, builder) -> builder.writeBinary(target.tsFileBinaryValue());
+            case ROW, UNKNOWN, VECTOR ->
+                (target, builder) -> {
+                  throw new UnsupportedOperationException();
+                };
+          };
+
+  static final TypeService<ContinuouslySatisfyRowTransformer>
+      CONTINUOUSLY_SATISFY_ROW_TRANSFORMER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32 -> (target, row) -> target.transformInt(row.getTime(), row.getInt(0));
+                case INT64 -> (target, row) -> target.transformLong(row.getTime(), row.getLong(0));
+                case FLOAT ->
+                    (target, row) -> target.transformFloat(row.getTime(), row.getFloat(0));
+                case DOUBLE ->
+                    (target, row) -> target.transformDouble(row.getTime(), row.getDouble(0));
+                case BOOLEAN ->
+                    (target, row) -> target.transformBoolean(row.getTime(), row.getBoolean(0));
+                case DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    (target, row) -> {
+                      throw invalidContinuouslySatisfyDataType(type);
+                    };
+              };
+
+  static final TypeService<ContinuouslySatisfyTerminator> CONTINUOUSLY_SATISFY_TERMINATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, INT64, FLOAT, DOUBLE, BOOLEAN ->
+                UDTFContinuouslySatisfy::terminateSupportedType;
+            case DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (target, collector) -> {
+                  throw invalidContinuouslySatisfyDataType(type);
+                };
+          };
+
   static {
     VALUE_TREND_READER_SERVICE.check();
     VALUE_DIFFERENCE_OPERATOR_SERVICE.check();
@@ -211,6 +473,25 @@ final class TypeServices {
     NON_NEGATIVE_DERIVATIVE_OPERATOR_SERVICE.check();
     NUMERIC_ROW_READER_SERVICE.check();
     NUMERIC_COLUMN_READER_SERVICE.check();
+    NUMERIC_ROW_COLLECTOR_SERVICE.check();
+    ABS_ROW_COLLECTOR_SERVICE.check();
+    ABS_ROW_MAPPER_SERVICE.check();
+    ABS_COLUMN_TRANSFORMER_SERVICE.check();
+    M4_WINDOW_TRANSFORMER_SERVICE.check();
+    BUCKET_M4_WINDOW_TRANSFORMER_SERVICE.check();
+    BUCKET_AGG_WINDOW_TRANSFORMER_SERVICE.check();
+    BUCKET_OUTLIER_WINDOW_TRANSFORMER_SERVICE.check();
+    CHANGE_POINT_PROCESSOR_SERVICE.check();
+    SELECT_K_ROW_TRANSFORMER_SERVICE.check();
+    SELECT_K_TERMINATOR_SERVICE.check();
+    TOP_K_QUEUE_CONSTRUCTOR_SERVICE.check();
+    BOTTOM_K_QUEUE_CONSTRUCTOR_SERVICE.check();
+    CONSTANT_PARSER_SERVICE.check();
+    CONSTANT_ROW_COLLECTOR_SERVICE.check();
+    CONSTANT_ROW_MAPPER_SERVICE.check();
+    CONSTANT_COLUMN_VALUE_WRITER_SERVICE.check();
+    CONTINUOUSLY_SATISFY_ROW_TRANSFORMER_SERVICE.check();
+    CONTINUOUSLY_SATISFY_TERMINATOR_SERVICE.check();
   }
 
   private TypeServices() {}
@@ -226,6 +507,51 @@ final class TypeServices {
         Type.DOUBLE);
   }
 
+  private static UDFInputSeriesDataTypeNotValidException invalidSelectKDataType(
+      org.apache.tsfile.read.common.type.Type type) {
+    return new UDFInputSeriesDataTypeNotValidException(
+        0,
+        UDFDataTypeTransformer.transformReadTypeToUDFDataType(type),
+        Type.INT32,
+        Type.INT64,
+        Type.FLOAT,
+        Type.DOUBLE,
+        Type.TEXT,
+        Type.DATE,
+        Type.TIMESTAMP,
+        Type.STRING);
+  }
+
+  private static UDFInputSeriesDataTypeNotValidException invalidContinuouslySatisfyDataType(
+      org.apache.tsfile.read.common.type.Type type) {
+    return new UDFInputSeriesDataTypeNotValidException(
+        0,
+        UDFDataTypeTransformer.transformReadTypeToUDFDataType(type),
+        Type.INT32,
+        Type.INT64,
+        Type.FLOAT,
+        Type.DOUBLE);
+  }
+
+  // Keep each window algorithm's primitive output type while selecting its callback once.
+  private static <T> TypeService<NumericWindowTransformer<T>> numericWindowTransformerService(
+      NumericWindowTransformer<T> intTransformer,
+      NumericWindowTransformer<T> longTransformer,
+      NumericWindowTransformer<T> floatTransformer,
+      NumericWindowTransformer<T> doubleTransformer) {
+    return type ->
+        switch (type.getTypeEnum()) {
+          case INT32 -> intTransformer;
+          case INT64 -> longTransformer;
+          case FLOAT -> floatTransformer;
+          case DOUBLE -> doubleTransformer;
+          case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+              (target, rowWindow, collector) -> {
+                throw invalidNumericDataType(type);
+              };
+        };
+  }
+
   @FunctionalInterface
   interface PreviousValueReader {
     void read(UDTFValueTrend target, Row row)
@@ -254,4 +580,92 @@ final class TypeServices {
   interface NumericColumnReader {
     double read(Column column, int position) throws UDFInputSeriesDataTypeNotValidException;
   }
+
+  @FunctionalInterface
+  interface NumericRowCollector {
+    void collect(Row row, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface AbsRowCollector {
+    void collect(Row row, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface AbsRowMapper {
+    Object map(Row row) throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface AbsColumnTransformer {
+    void transform(UDTFAbs target, Column[] columns, ColumnBuilder builder)
+        throws UDFInputSeriesDataTypeNotValidException;
+  }
+
+  @FunctionalInterface
+  interface NumericWindowTransformer<T> {
+    void transform(T target, RowWindow rowWindow, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface ChangePointProcessor {
+    void transform(UDTFChangePoints target, Row row, PointCollector collector) throws IOException;
+  }
+
+  @FunctionalInterface
+  interface SelectKRowTransformer {
+    void transform(UDTFSelectK target, Row row)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface SelectKTerminator {
+    void terminate(UDTFSelectK target, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface TopKQueueConstructor {
+    void construct(UDTFTopK target);
+  }
+
+  @FunctionalInterface
+  interface BottomKQueueConstructor {
+    void construct(UDTFBottomK target);
+  }
+
+  @FunctionalInterface
+  interface ConstantParser {
+    void parse(UDTFConst target, UDFParameters parameters);
+  }
+
+  @FunctionalInterface
+  interface ConstantRowCollector {
+    void collect(UDTFConst target, Row row, PointCollector collector) throws IOException;
+  }
+
+  @FunctionalInterface
+  interface ConstantRowMapper {
+    Object map(UDTFConst target);
+  }
+
+  @FunctionalInterface
+  interface ConstantColumnValueWriter {
+    void write(UDTFConst target, ColumnBuilder builder);
+  }
+
+  @FunctionalInterface
+  interface ContinuouslySatisfyRowTransformer {
+    boolean transform(UDTFContinuouslySatisfy target, Row row)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface ContinuouslySatisfyTerminator {
+    void terminate(UDTFContinuouslySatisfy target, PointCollector collector)
+        throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java
index 2af7ee3c83d..06303b8d2a3 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java
@@ -27,62 +27,36 @@ import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.MappableRowByRowAccessStrategy;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
+import org.apache.tsfile.read.common.type.Type;
 
 import java.io.IOException;
 
 @SuppressWarnings("java:S2177")
 public class UDTFAbs extends UDTFMath {
 
+  private TypeServices.AbsRowCollector rowCollector;
+  private TypeServices.AbsRowMapper rowMapper;
+  private TypeServices.AbsColumnTransformer columnTransformer;
+
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
     dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    Type type = Type.fromTsDataType(dataType);
+    rowCollector = TypeServices.ABS_ROW_COLLECTOR_SERVICE.call(type);
+    rowMapper = TypeServices.ABS_ROW_MAPPER_SERVICE.call(type);
+    columnTransformer = TypeServices.ABS_COLUMN_TRANSFORMER_SERVICE.call(type);
     configurations
         .setAccessStrategy(new MappableRowByRowAccessStrategy())
         .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
   }
 
   @Override
-  public void transform(Row row, PointCollector collector)
-      throws UDFInputSeriesDataTypeNotValidException, IOException {
-    long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        collector.putInt(time, Math.abs(row.getInt(0)));
-        break;
-      case INT64:
-        collector.putLong(time, Math.abs(row.getLong(0)));
-        break;
-      case FLOAT:
-        collector.putFloat(time, Math.abs(row.getFloat(0)));
-        break;
-      case DOUBLE:
-        collector.putDouble(time, Math.abs(row.getDouble(0)));
-        break;
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case TIMESTAMP:
-      case TEXT:
-      case DATE:
-      case BOOLEAN:
-      case VECTOR:
-      case UNKNOWN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+  public void transform(Row row, PointCollector collector) throws IOException {
+    rowCollector.collect(row, collector);
   }
 
   @Override
@@ -90,66 +64,12 @@ public class UDTFAbs extends UDTFMath {
     if (row.isNull(0)) {
       return null;
     }
-    switch (dataType) {
-      case INT32:
-        return Math.abs(row.getInt(0));
-      case INT64:
-        return Math.abs(row.getLong(0));
-      case FLOAT:
-        return Math.abs(row.getFloat(0));
-      case DOUBLE:
-        return Math.abs(row.getDouble(0));
-      case DATE:
-      case BOOLEAN:
-      case TEXT:
-      case TIMESTAMP:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    return rowMapper.map(row);
   }
 
   @Override
   public void transform(Column[] columns, ColumnBuilder builder) throws Exception {
-    switch (dataType) {
-      case INT32:
-        transformInt(columns, builder);
-        return;
-      case INT64:
-        transformLong(columns, builder);
-        return;
-      case FLOAT:
-        transformFloat(columns, builder);
-        return;
-      case DOUBLE:
-        transformDouble(columns, builder);
-        return;
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case TEXT:
-      case TIMESTAMP:
-      case BOOLEAN:
-      case DATE:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    columnTransformer.transform(this, columns, builder);
   }
 
   @Override
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java
index 045e49b064b..b4702c8046b 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java
@@ -19,10 +19,6 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
-
 import org.apache.tsfile.utils.Pair;
 
 import java.util.Comparator;
@@ -32,63 +28,50 @@ import java.util.PriorityQueue;
 public class UDTFBottomK extends UDTFSelectK {
 
   @Override
-  protected void constructPQ() throws UDFInputSeriesDataTypeNotValidException {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        intPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        longPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
-        break;
-      case FLOAT:
-        floatPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
-        break;
-      case DOUBLE:
-        doublePQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
-        break;
-      case TEXT:
-      case STRING:
-        stringPQ =
-            new PriorityQueue<>(
-                k,
-                (pairA, pairB) -> {
-                  final String cs1 = pairB.right;
-                  final String cs2 = pairA.right;
-
-                  if (Objects.requireNonNull(cs1).equals(Objects.requireNonNull(cs2))) {
-                    return 0;
-                  }
-
-                  for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) {
-                    final char a = cs1.charAt(i);
-                    final char b = cs2.charAt(i);
-                    if (a != b) {
-                      return a - b;
-                    }
-                  }
-
-                  return cs1.length() - cs2.length();
-                });
-        break;
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE,
-            Type.TEXT,
-            Type.DATE,
-            Type.TIMESTAMP,
-            Type.STRING);
-    }
+  protected void constructPQ() {
+    TypeServices.BOTTOM_K_QUEUE_CONSTRUCTOR_SERVICE
+        .call(org.apache.tsfile.read.common.type.Type.fromTsDataType(dataType))
+        .construct(this);
+  }
+
+  void initializeIntQueue() {
+    intPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
+  }
+
+  void initializeLongQueue() {
+    longPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
+  }
+
+  void initializeFloatQueue() {
+    floatPQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
+  }
+
+  void initializeDoubleQueue() {
+    doublePQ = new PriorityQueue<>(k, Comparator.comparing(o -> -o.right));
+  }
+
+  void initializeStringQueue() {
+    stringPQ =
+        new PriorityQueue<>(
+            k,
+            (pairA, pairB) -> {
+              final String cs1 = pairB.right;
+              final String cs2 = pairA.right;
+
+              if (Objects.requireNonNull(cs1).equals(Objects.requireNonNull(cs2))) {
+                return 0;
+              }
+
+              for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) {
+                final char a = cs1.charAt(i);
+                final char b = cs2.charAt(i);
+                if (a != b) {
+                  return a - b;
+                }
+              }
+
+              return cs1.length() - cs2.length();
+            });
   }
 
   @Override
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFChangePoints.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFChangePoints.java
index dd19304384b..f573e0e320b 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFChangePoints.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFChangePoints.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
+import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.UDTF;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
@@ -28,19 +29,21 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.type.Type;
 
+import java.io.IOException;
+
 /**
  * Return a series that the consecutive identical values in input series are removed (keeping only
  * the first one).
  */
 public class UDTFChangePoints implements UDTF {
   private boolean isFirst = true;
-  private Type dataType;
   private boolean cacheBoolean;
   private int cacheInt;
   private long cacheLong;
   private float cacheFloat;
   private double cacheDouble;
   private String cacheString;
+  private TypeServices.ChangePointProcessor changePointProcessor;
 
   @Override
   public void validate(UDFParameterValidator validator) throws Exception {
@@ -50,96 +53,99 @@ public class UDTFChangePoints implements UDTF {
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws Exception {
-    dataType = parameters.getDataType(0);
+    Type dataType = parameters.getDataType(0);
+    changePointProcessor =
+        TypeServices.CHANGE_POINT_PROCESSOR_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(dataType));
     configurations.setAccessStrategy(new RowByRowAccessStrategy()).setOutputDataType(dataType);
   }
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    switch (dataType) {
-      case BOOLEAN:
-        if (isFirst) {
-          isFirst = false;
-          cacheBoolean = row.getBoolean(0);
-          collector.putBoolean(row.getTime(), cacheBoolean);
-        } else {
-          boolean rowData = row.getBoolean(0);
-          if (rowData != cacheBoolean) {
-            cacheBoolean = rowData;
-            collector.putBoolean(row.getTime(), cacheBoolean);
-          }
-        }
-        break;
-      case INT32:
-        if (isFirst) {
-          isFirst = false;
-          cacheInt = row.getInt(0);
-          collector.putInt(row.getTime(), cacheInt);
-        } else {
-          int rowData = row.getInt(0);
-          if (rowData != cacheInt) {
-            cacheInt = rowData;
-            collector.putInt(row.getTime(), cacheInt);
-          }
-        }
-        break;
-      case INT64:
-        if (isFirst) {
-          isFirst = false;
-          cacheLong = row.getLong(0);
-          collector.putLong(row.getTime(), cacheLong);
-        } else {
-          long rowData = row.getLong(0);
-          if (rowData != cacheLong) {
-            cacheLong = rowData;
-            collector.putLong(row.getTime(), cacheLong);
-          }
-        }
-        break;
-      case FLOAT:
-        if (isFirst) {
-          isFirst = false;
-          cacheFloat = row.getFloat(0);
-          collector.putFloat(row.getTime(), cacheFloat);
-        } else {
-          float rowData = row.getFloat(0);
-          if (rowData != cacheFloat) {
-            cacheFloat = rowData;
-            collector.putFloat(row.getTime(), cacheFloat);
-          }
-        }
-        break;
-      case DOUBLE:
-        if (isFirst) {
-          isFirst = false;
-          cacheDouble = row.getDouble(0);
-          collector.putDouble(row.getTime(), cacheDouble);
-        } else {
-          double rowData = row.getDouble(0);
-          if (rowData != cacheDouble) {
-            cacheDouble = rowData;
-            collector.putDouble(row.getTime(), cacheDouble);
-          }
-        }
-        break;
-      case TEXT:
-        if (isFirst) {
-          isFirst = false;
-          cacheString = row.getString(0);
-          collector.putString(row.getTime(), cacheString);
-        } else {
-          String rowData = row.getString(0);
-          if (!rowData.equals(cacheString)) {
-            cacheString = rowData;
-            collector.putString(row.getTime(), cacheString);
-          }
-        }
-      case STRING:
-      case BLOB:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        break;
+    changePointProcessor.transform(this, row, collector);
+  }
+
+  void transformBoolean(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheBoolean = row.getBoolean(0);
+      collector.putBoolean(row.getTime(), cacheBoolean);
+    } else {
+      boolean rowData = row.getBoolean(0);
+      if (rowData != cacheBoolean) {
+        cacheBoolean = rowData;
+        collector.putBoolean(row.getTime(), cacheBoolean);
+      }
+    }
+  }
+
+  void transformInt(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheInt = row.getInt(0);
+      collector.putInt(row.getTime(), cacheInt);
+    } else {
+      int rowData = row.getInt(0);
+      if (rowData != cacheInt) {
+        cacheInt = rowData;
+        collector.putInt(row.getTime(), cacheInt);
+      }
+    }
+  }
+
+  void transformLong(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheLong = row.getLong(0);
+      collector.putLong(row.getTime(), cacheLong);
+    } else {
+      long rowData = row.getLong(0);
+      if (rowData != cacheLong) {
+        cacheLong = rowData;
+        collector.putLong(row.getTime(), cacheLong);
+      }
+    }
+  }
+
+  void transformFloat(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheFloat = row.getFloat(0);
+      collector.putFloat(row.getTime(), cacheFloat);
+    } else {
+      float rowData = row.getFloat(0);
+      if (rowData != cacheFloat) {
+        cacheFloat = rowData;
+        collector.putFloat(row.getTime(), cacheFloat);
+      }
+    }
+  }
+
+  void transformDouble(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheDouble = row.getDouble(0);
+      collector.putDouble(row.getTime(), cacheDouble);
+    } else {
+      double rowData = row.getDouble(0);
+      if (rowData != cacheDouble) {
+        cacheDouble = rowData;
+        collector.putDouble(row.getTime(), cacheDouble);
+      }
+    }
+  }
+
+  void transformString(Row row, PointCollector collector) throws IOException {
+    if (isFirst) {
+      isFirst = false;
+      cacheString = row.getString(0);
+      collector.putString(row.getTime(), cacheString);
+    } else {
+      String rowData = row.getString(0);
+      if (!rowData.equals(cacheString)) {
+        cacheString = rowData;
+        collector.putString(row.getTime(), cacheString);
+      }
     }
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java
index f3436859f2a..09ebb443a8d 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java
@@ -34,6 +34,7 @@ import org.apache.iotdb.udf.api.exception.UDFParameterNotValidException;
 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.type.Type;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.BytesUtils;
 import org.apache.tsfile.utils.DateUtils;
@@ -60,14 +61,15 @@ public class UDTFConst implements UDTF {
     VALID_TYPES.add(TSDataType.OBJECT.name());
   }
 
-  private TSDataType dataType;
-
   private int intValue;
   private long longValue;
   private float floatValue;
   private double doubleValue;
   private boolean booleanValue;
   private Binary binaryValue;
+  private TypeServices.ConstantRowCollector rowCollector;
+  private TypeServices.ConstantRowMapper rowMapper;
+  private TypeServices.ConstantColumnValueWriter columnValueWriter;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFParameterNotValidException {
@@ -82,38 +84,12 @@ public class UDTFConst implements UDTF {
 
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
-    dataType = TSDataType.valueOf(parameters.getString("type"));
-    switch (dataType) {
-      case INT32:
-        intValue = Integer.parseInt(parameters.getString("value"));
-        break;
-      case DATE:
-        intValue = DateUtils.parseDateExpressionToInt(parameters.getString("value"));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        longValue = Long.parseLong(parameters.getString("value"));
-        break;
-      case FLOAT:
-        floatValue = Float.parseFloat(parameters.getString("value"));
-        break;
-      case DOUBLE:
-        doubleValue = Double.parseDouble(parameters.getString("value"));
-        break;
-      case BOOLEAN:
-        booleanValue = Boolean.parseBoolean(parameters.getString("value"));
-        break;
-      case TEXT:
-      case STRING:
-        binaryValue = BytesUtils.valueOf(parameters.getString("value"));
-        break;
-      case BLOB:
-      case OBJECT:
-        binaryValue = new Binary(BlobUtils.parseBlobString(parameters.getString("value")));
-        break;
-      default:
-        throw new UnsupportedOperationException();
-    }
+    TSDataType dataType = TSDataType.valueOf(parameters.getString("type"));
+    Type type = Type.fromTsDataType(dataType);
+    TypeServices.CONSTANT_PARSER_SERVICE.call(type).parse(this, parameters);
+    rowCollector = TypeServices.CONSTANT_ROW_COLLECTOR_SERVICE.call(type);
+    rowMapper = TypeServices.CONSTANT_ROW_MAPPER_SERVICE.call(type);
+    columnValueWriter = TypeServices.CONSTANT_COLUMN_VALUE_WRITER_SERVICE.call(type);
 
     configurations
         .setAccessStrategy(new MappableRowByRowAccessStrategy())
@@ -122,162 +98,93 @@ public class UDTFConst implements UDTF {
 
   @Override
   public void transform(Row row, PointCollector collector) throws Exception {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        collector.putInt(row.getTime(), intValue);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        collector.putLong(row.getTime(), longValue);
-        break;
-      case FLOAT:
-        collector.putFloat(row.getTime(), floatValue);
-        break;
-      case DOUBLE:
-        collector.putDouble(row.getTime(), doubleValue);
-        break;
-      case BOOLEAN:
-        collector.putBoolean(row.getTime(), booleanValue);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        collector.putBinary(row.getTime(), UDFBinaryTransformer.transformToUDFBinary(binaryValue));
-        break;
-      default:
-        throw new UnsupportedOperationException();
-    }
+    rowCollector.collect(this, row, collector);
   }
 
   @Override
   public Object transform(Row row) throws IOException {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        return intValue;
-      case INT64:
-      case TIMESTAMP:
-        return longValue;
-      case FLOAT:
-        return floatValue;
-      case DOUBLE:
-        return doubleValue;
-      case BOOLEAN:
-        return booleanValue;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        return UDFBinaryTransformer.transformToUDFBinary(binaryValue);
-      default:
-        throw new UnsupportedOperationException();
-    }
+    return rowMapper.map(this);
   }
 
   @Override
   public void transform(Column[] columns, ColumnBuilder builder) throws Exception {
-    int count = columns[0].getPositionCount();
+    writeConstant(columns, builder);
+  }
 
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeInt(intValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
-        }
-        return;
-      case INT64:
-      case TIMESTAMP:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeLong(longValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
-        }
-        return;
-      case FLOAT:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeFloat(floatValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
-        }
-        return;
-      case DOUBLE:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeDouble(doubleValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
-        }
-        return;
-      case BOOLEAN:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeBoolean(booleanValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
-        }
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        for (int i = 0; i < count; i++) {
-          boolean hasWritten = false;
-          for (int j = 0; j < columns.length - 1; j++) {
-            if (!columns[j].isNull(i)) {
-              builder.writeBinary(binaryValue);
-              hasWritten = true;
-              break;
-            }
-          }
-          if (!hasWritten) {
-            builder.appendNull();
-          }
+  void parseInt(UDFParameters parameters) {
+    intValue = Integer.parseInt(parameters.getString("value"));
+  }
+
+  void parseDate(UDFParameters parameters) {
+    intValue = DateUtils.parseDateExpressionToInt(parameters.getString("value"));
+  }
+
+  void parseLong(UDFParameters parameters) {
+    longValue = Long.parseLong(parameters.getString("value"));
+  }
+
+  void parseFloat(UDFParameters parameters) {
+    floatValue = Float.parseFloat(parameters.getString("value"));
+  }
+
+  void parseDouble(UDFParameters parameters) {
+    doubleValue = Double.parseDouble(parameters.getString("value"));
+  }
+
+  void parseBoolean(UDFParameters parameters) {
+    booleanValue = Boolean.parseBoolean(parameters.getString("value"));
+  }
+
+  void parseText(UDFParameters parameters) {
+    binaryValue = BytesUtils.valueOf(parameters.getString("value"));
+  }
+
+  void parseBlob(UDFParameters parameters) {
+    binaryValue = new Binary(BlobUtils.parseBlobString(parameters.getString("value")));
+  }
+
+  int intValue() {
+    return intValue;
+  }
+
+  long longValue() {
+    return longValue;
+  }
+
+  float floatValue() {
+    return floatValue;
+  }
+
+  double doubleValue() {
+    return doubleValue;
+  }
+
+  boolean booleanValue() {
+    return booleanValue;
+  }
+
+  org.apache.iotdb.udf.api.type.Binary binaryValue() {
+    return UDFBinaryTransformer.transformToUDFBinary(binaryValue);
+  }
+
+  Binary tsFileBinaryValue() {
+    return binaryValue;
+  }
+
+  private void writeConstant(Column[] columns, ColumnBuilder builder) {
+    int count = columns[0].getPositionCount();
+    for (int i = 0; i < count; i++) {
+      boolean hasWritten = false;
+      for (int j = 0; j < columns.length - 1; j++) {
+        if (!columns[j].isNull(i)) {
+          columnValueWriter.write(this, builder);
+          hasWritten = true;
+          break;
         }
-        return;
-      default:
-        throw new UnsupportedOperationException();
+      }
+      if (!hasWritten) {
+        builder.appendNull();
+      }
     }
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java
index de6232ac6db..657b75e6027 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java
@@ -32,7 +32,6 @@ import org.apache.iotdb.udf.api.exception.UDFException;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
-import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.utils.Pair;
 
 import java.io.IOException;
@@ -40,11 +39,12 @@ import java.io.IOException;
 public abstract class UDTFContinuouslySatisfy implements UDTF {
   protected long min;
   protected long max;
-  protected TSDataType dataType;
   protected long satisfyValueCount;
   protected long satisfyValueLastTime;
   protected long satisfyValueStartTime;
   protected Pair<Long, Long> interval;
+  private TypeServices.ContinuouslySatisfyRowTransformer rowTransformer;
+  private TypeServices.ContinuouslySatisfyTerminator terminator;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -74,7 +74,12 @@ public abstract class UDTFContinuouslySatisfy implements UDTF {
     satisfyValueStartTime = 0L;
     satisfyValueLastTime = -1L;
 
-    dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    rowTransformer =
+        TypeServices.CONTINUOUSLY_SATISFY_ROW_TRANSFORMER_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0)));
+    terminator =
+        TypeServices.CONTINUOUSLY_SATISFY_TERMINATOR_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0)));
     min = parameters.getLongOrDefault("min", getDefaultMin());
     max = parameters.getLongOrDefault("max", getDefaultMax());
     configurations.setAccessStrategy(new RowByRowAccessStrategy()).setOutputDataType(Type.INT64);
@@ -83,40 +88,7 @@ public abstract class UDTFContinuouslySatisfy implements UDTF {
   @Override
   public void transform(Row row, PointCollector collector)
       throws IOException, UDFInputSeriesDataTypeNotValidException {
-    boolean needAddNewRecord;
-    switch (dataType) {
-      case INT32:
-        needAddNewRecord = transformInt(row.getTime(), row.getInt(0));
-        break;
-      case INT64:
-        needAddNewRecord = transformLong(row.getTime(), row.getLong(0));
-        break;
-      case FLOAT:
-        needAddNewRecord = transformFloat(row.getTime(), row.getFloat(0));
-        break;
-      case DOUBLE:
-        needAddNewRecord = transformDouble(row.getTime(), row.getDouble(0));
-        break;
-      case BOOLEAN:
-        needAddNewRecord = transformBoolean(row.getTime(), row.getBoolean(0));
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case TIMESTAMP:
-      case DATE:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
-    if (needAddNewRecord) {
+    if (rowTransformer.transform(this, row)) {
       collector.putLong(interval.left, interval.right);
     }
   }
@@ -215,33 +187,12 @@ public abstract class UDTFContinuouslySatisfy implements UDTF {
   @Override
   public void terminate(PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    switch (dataType) {
-      case INT32:
-      case INT64:
-      case FLOAT:
-      case DOUBLE:
-      case BOOLEAN:
-        if (satisfyValueCount > 0) {
-          if (getRecord() >= min && getRecord() <= max) {
-            collector.putLong(satisfyValueStartTime, getRecord());
-          }
-        }
-        break;
-      case TIMESTAMP:
-      case DATE:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case TEXT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
+    terminator.terminate(this, collector);
+  }
+
+  void terminateSupportedType(PointCollector collector) throws IOException {
+    if (satisfyValueCount > 0 && getRecord() >= min && getRecord() <= max) {
+      collector.putLong(satisfyValueStartTime, getRecord());
     }
   }
 
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java
index 21506698605..a98f831dd14 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java
@@ -28,9 +28,7 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.exception.UDFParameterNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.enums.TSDataType;
 
@@ -40,6 +38,7 @@ public class UDTFEqualSizeBucketAggSample extends UDTFEqualSizeBucketSample {
 
   private String aggMethodType;
   private Aggregator aggregator;
+  private TypeServices.NumericWindowTransformer<UDTFEqualSizeBucketAggSample> windowTransformer;
 
   private interface Aggregator {
 
@@ -463,40 +462,30 @@ public class UDTFEqualSizeBucketAggSample extends UDTFEqualSizeBucketSample {
         throw new UDFParameterNotValidException(
             "Illegal aggregation method. Aggregation type should be avg, min, max, sum, extreme, variance.");
     }
+    windowTransformer =
+        TypeServices.BUCKET_AGG_WINDOW_TRANSFORMER_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0)));
   }
 
   @Override
   public void transform(RowWindow rowWindow, PointCollector collector)
       throws IOException, UDFParameterNotValidException {
-    switch (dataType) {
-      case INT32:
-        aggregator.aggregateInt(rowWindow, collector);
-        break;
-      case INT64:
-        aggregator.aggregateLong(rowWindow, collector);
-        break;
-      case FLOAT:
-        aggregator.aggregateFloat(rowWindow, collector);
-        break;
-      case DOUBLE:
-        aggregator.aggregateDouble(rowWindow, collector);
-        break;
-      case BLOB:
-      case OBJECT:
-      case TEXT:
-      case DATE:
-      case STRING:
-      case TIMESTAMP:
-      case BOOLEAN:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    windowTransformer.transform(this, rowWindow, collector);
+  }
+
+  void aggregateInt(RowWindow rowWindow, PointCollector collector) throws IOException {
+    aggregator.aggregateInt(rowWindow, collector);
+  }
+
+  void aggregateLong(RowWindow rowWindow, PointCollector collector) throws IOException {
+    aggregator.aggregateLong(rowWindow, collector);
+  }
+
+  void aggregateFloat(RowWindow rowWindow, PointCollector collector) throws IOException {
+    aggregator.aggregateFloat(rowWindow, collector);
+  }
+
+  void aggregateDouble(RowWindow rowWindow, PointCollector collector) throws IOException {
+    aggregator.aggregateDouble(rowWindow, collector);
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java
index fea5cb694fa..b2e41eacbc7 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java
@@ -27,16 +27,20 @@ import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.read.common.type.Type;
 
 import java.io.IOException;
 
 public class UDTFEqualSizeBucketM4Sample extends UDTFEqualSizeBucketSample {
 
+  private TypeServices.NumericWindowTransformer<UDTFEqualSizeBucketM4Sample> windowTransformer;
+
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
     bucketSize *= 4;
+    windowTransformer =
+        TypeServices.BUCKET_M4_WINDOW_TRANSFORMER_SERVICE.call(Type.fromTsDataType(dataType));
     configurations
         .setAccessStrategy(new SlidingSizeWindowAccessStrategy(bucketSize))
         .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
@@ -45,36 +49,7 @@ public class UDTFEqualSizeBucketM4Sample extends UDTFEqualSizeBucketSample {
   @Override
   public void transform(RowWindow rowWindow, PointCollector collector)
       throws UDFException, IOException {
-    switch (dataType) {
-      case INT32:
-        transformInt(rowWindow, collector);
-        break;
-      case INT64:
-        transformLong(rowWindow, collector);
-        break;
-      case FLOAT:
-        transformFloat(rowWindow, collector);
-        break;
-      case DOUBLE:
-        transformDouble(rowWindow, collector);
-        break;
-      case TIMESTAMP:
-      case BOOLEAN:
-      case DATE:
-      case STRING:
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    windowTransformer.transform(this, rowWindow, collector);
   }
 
   public void transformInt(RowWindow rowWindow, PointCollector collector) throws IOException {
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java
index 85490ac534e..e52d96e1fe3 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java
@@ -29,9 +29,7 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.exception.UDFParameterNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.utils.Pair;
 
@@ -45,6 +43,7 @@ public class UDTFEqualSizeBucketOutlierSample extends UDTFEqualSizeBucketSample
   private String type;
   private int number;
   private OutlierSampler outlierSampler;
+  private TypeServices.NumericWindowTransformer<UDTFEqualSizeBucketOutlierSample> windowTransformer;
 
   private interface OutlierSampler {
 
@@ -638,41 +637,31 @@ public class UDTFEqualSizeBucketOutlierSample extends UDTFEqualSizeBucketSample
         throw new UDFParameterNotValidException(
             "Illegal outlier method. Outlier type should be avg, stendis, cos or prenextdis.");
     }
+    windowTransformer =
+        TypeServices.BUCKET_OUTLIER_WINDOW_TRANSFORMER_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0)));
   }
 
   @Override
   public void transform(RowWindow rowWindow, PointCollector collector)
       throws IOException, UDFParameterNotValidException {
-    switch (dataType) {
-      case INT32:
-        outlierSampler.outlierSampleInt(rowWindow, collector);
-        break;
-      case INT64:
-        outlierSampler.outlierSampleLong(rowWindow, collector);
-        break;
-      case FLOAT:
-        outlierSampler.outlierSampleFloat(rowWindow, collector);
-        break;
-      case DOUBLE:
-        outlierSampler.outlierSampleDouble(rowWindow, collector);
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case DATE:
-      case STRING:
-      case BOOLEAN:
-      case TIMESTAMP:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    windowTransformer.transform(this, rowWindow, collector);
+  }
+
+  void outlierSampleInt(RowWindow rowWindow, PointCollector collector) throws IOException {
+    outlierSampler.outlierSampleInt(rowWindow, collector);
+  }
+
+  void outlierSampleLong(RowWindow rowWindow, PointCollector collector) throws IOException {
+    outlierSampler.outlierSampleLong(rowWindow, collector);
+  }
+
+  void outlierSampleFloat(RowWindow rowWindow, PointCollector collector) throws IOException {
+    outlierSampler.outlierSampleFloat(rowWindow, collector);
+  }
+
+  void outlierSampleDouble(RowWindow rowWindow, PointCollector collector) throws IOException {
+    outlierSampler.outlierSampleDouble(rowWindow, collector);
   }
 
   public void addToMinHeap(PriorityQueue<Pair<Integer, Double>> pq, int i, double value) {
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java
index 566e02e7b58..7d2ad94de08 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java
@@ -26,8 +26,8 @@ import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
+
+import org.apache.tsfile.read.common.type.Type;
 
 import java.io.IOException;
 import java.security.SecureRandom;
@@ -35,48 +35,20 @@ import java.security.SecureRandom;
 public class UDTFEqualSizeBucketRandomSample extends UDTFEqualSizeBucketSample {
 
   private SecureRandom random;
+  private TypeServices.NumericRowCollector rowCollector;
 
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
     random = new SecureRandom();
+    rowCollector = TypeServices.NUMERIC_ROW_COLLECTOR_SERVICE.call(Type.fromTsDataType(dataType));
     configurations
         .setAccessStrategy(new SlidingSizeWindowAccessStrategy(bucketSize))
         .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
   }
 
   @Override
-  public void transform(RowWindow rowWindow, PointCollector collector)
-      throws IOException, UDFInputSeriesDataTypeNotValidException {
+  public void transform(RowWindow rowWindow, PointCollector collector) throws IOException {
     Row row = rowWindow.getRow(random.nextInt(rowWindow.windowSize()));
-    switch (dataType) {
-      case INT32:
-        collector.putInt(row.getTime(), row.getInt(0));
-        break;
-      case INT64:
-        collector.putLong(row.getTime(), row.getLong(0));
-        break;
-      case FLOAT:
-        collector.putFloat(row.getTime(), row.getFloat(0));
-        break;
-      case DOUBLE:
-        collector.putDouble(row.getTime(), row.getDouble(0));
-        break;
-      case BOOLEAN:
-      case TIMESTAMP:
-      case DATE:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case TEXT:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    rowCollector.collect(row, collector);
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFM4.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFM4.java
index 3de46df72a0..eb7970ac0ef 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFM4.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFM4.java
@@ -30,7 +30,6 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy;
 import org.apache.iotdb.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.exception.UDFParameterNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
@@ -60,6 +59,7 @@ public class UDTFM4 implements UDTF {
 
   protected AccessStrategy accessStrategy;
   protected TSDataType dataType;
+  private TypeServices.NumericWindowTransformer<UDTFM4> windowTransformer;
 
   public static final String WINDOW_SIZE_KEY = "windowSize";
   public static final String TIME_INTERVAL_KEY = "timeInterval";
@@ -100,6 +100,9 @@ public class UDTFM4 implements UDTF {
   @Override
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
+    windowTransformer =
+        TypeServices.M4_WINDOW_TRANSFORMER_SERVICE.call(
+            UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0)));
     // set data type
     configurations.setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
 
@@ -124,36 +127,7 @@ public class UDTFM4 implements UDTF {
   @Override
   public void transform(RowWindow rowWindow, PointCollector collector)
       throws UDFException, IOException {
-    switch (dataType) {
-      case INT32:
-        transformInt(rowWindow, collector);
-        break;
-      case INT64:
-        transformLong(rowWindow, collector);
-        break;
-      case FLOAT:
-        transformFloat(rowWindow, collector);
-        break;
-      case DOUBLE:
-        transformDouble(rowWindow, collector);
-        break;
-      case BLOB:
-      case OBJECT:
-      case DATE:
-      case STRING:
-      case TIMESTAMP:
-      case BOOLEAN:
-      case TEXT:
-      default:
-        // This will not happen
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    windowTransformer.transform(this, rowWindow, collector);
   }
 
   public void transformInt(RowWindow rowWindow, PointCollector collector) throws IOException {
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java
index a888c998d8d..d4c4e2bb6de 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java
@@ -49,6 +49,8 @@ public abstract class UDTFSelectK implements UDTF {
   protected PriorityQueue<Pair<Long, Float>> floatPQ;
   protected PriorityQueue<Pair<Long, Double>> doublePQ;
   protected PriorityQueue<Pair<Long, String>> stringPQ;
+  private TypeServices.SelectKRowTransformer rowTransformer;
+  private TypeServices.SelectKTerminator terminator;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -77,6 +79,10 @@ public abstract class UDTFSelectK implements UDTF {
     k = parameters.getInt("k");
     dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
     constructPQ();
+    org.apache.tsfile.read.common.type.Type type =
+        UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0));
+    rowTransformer = TypeServices.SELECT_K_ROW_TRANSFORMER_SERVICE.call(type);
+    terminator = TypeServices.SELECT_K_TERMINATOR_SERVICE.call(type);
     configurations
         .setAccessStrategy(new RowByRowAccessStrategy())
         .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(dataType));
@@ -87,42 +93,7 @@ public abstract class UDTFSelectK implements UDTF {
   @Override
   public void transform(Row row, PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        transformInt(row.getTime(), row.getInt(0));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        transformLong(row.getTime(), row.getLong(0));
-        break;
-      case FLOAT:
-        transformFloat(row.getTime(), row.getFloat(0));
-        break;
-      case DOUBLE:
-        transformDouble(row.getTime(), row.getDouble(0));
-        break;
-      case TEXT:
-      case STRING:
-        transformString(row.getTime(), row.getString(0));
-        break;
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE,
-            Type.TEXT,
-            Type.DATE,
-            Type.TIMESTAMP,
-            Type.STRING);
-    }
+    rowTransformer.transform(this, row);
   }
 
   protected abstract void transformInt(long time, int value);
@@ -138,64 +109,41 @@ public abstract class UDTFSelectK implements UDTF {
   @Override
   public void terminate(PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        for (Pair<Long, Integer> pair :
-            intPQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
-          collector.putInt(pair.left, pair.right);
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        for (Pair<Long, Long> pair :
-            longPQ.stream()
-                .sorted(Comparator.comparing(p -> p.left))
-                .collect(Collectors.toList())) {
-          collector.putLong(pair.left, pair.right);
-        }
-        break;
-      case FLOAT:
-        for (Pair<Long, Float> pair :
-            floatPQ.stream()
-                .sorted(Comparator.comparing(p -> p.left))
-                .collect(Collectors.toList())) {
-          collector.putFloat(pair.left, pair.right);
-        }
-        break;
-      case DOUBLE:
-        for (Pair<Long, Double> pair :
-            doublePQ.stream()
-                .sorted(Comparator.comparing(p -> p.left))
-                .collect(Collectors.toList())) {
-          collector.putDouble(pair.left, pair.right);
-        }
-        break;
-      case TEXT:
-      case STRING:
-        for (Pair<Long, String> pair :
-            stringPQ.stream()
-                .sorted(Comparator.comparing(p -> p.left))
-                .collect(Collectors.toList())) {
-          collector.putString(pair.left, pair.right);
-        }
-        break;
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE,
-            Type.TEXT,
-            Type.DATE,
-            Type.TIMESTAMP,
-            Type.STRING);
+    terminator.terminate(this, collector);
+  }
+
+  void terminateInt(PointCollector collector) throws IOException {
+    for (Pair<Long, Integer> pair :
+        intPQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
+      collector.putInt(pair.left, pair.right);
+    }
+  }
+
+  void terminateLong(PointCollector collector) throws IOException {
+    for (Pair<Long, Long> pair :
+        longPQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
+      collector.putLong(pair.left, pair.right);
+    }
+  }
+
+  void terminateFloat(PointCollector collector) throws IOException {
+    for (Pair<Long, Float> pair :
+        floatPQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
+      collector.putFloat(pair.left, pair.right);
+    }
+  }
+
+  void terminateDouble(PointCollector collector) throws IOException {
+    for (Pair<Long, Double> pair :
+        doublePQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
+      collector.putDouble(pair.left, pair.right);
+    }
+  }
+
+  void terminateString(PointCollector collector) throws IOException {
+    for (Pair<Long, String> pair :
+        stringPQ.stream().sorted(Comparator.comparing(p -> p.left)).collect(Collectors.toList())) {
+      collector.putString(pair.left, pair.right);
     }
   }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java
index b8d94308ed9..fb37c91d9f4 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java
@@ -19,10 +19,6 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
-import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
-import org.apache.iotdb.udf.api.type.Type;
-
 import org.apache.tsfile.utils.Pair;
 
 import java.util.Comparator;
@@ -31,43 +27,30 @@ import java.util.PriorityQueue;
 public class UDTFTopK extends UDTFSelectK {
 
   @Override
-  protected void constructPQ() throws UDFInputSeriesDataTypeNotValidException {
-    switch (dataType) {
-      case INT32:
-      case DATE:
-        intPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        longPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
-        break;
-      case FLOAT:
-        floatPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
-        break;
-      case DOUBLE:
-        doublePQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
-        break;
-      case TEXT:
-      case STRING:
-        stringPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
-        break;
-      case BOOLEAN:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE,
-            Type.TEXT,
-            Type.DATE,
-            Type.TIMESTAMP,
-            Type.STRING);
-    }
+  protected void constructPQ() {
+    TypeServices.TOP_K_QUEUE_CONSTRUCTOR_SERVICE
+        .call(org.apache.tsfile.read.common.type.Type.fromTsDataType(dataType))
+        .construct(this);
+  }
+
+  void initializeIntQueue() {
+    intPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
+  }
+
+  void initializeLongQueue() {
+    longPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
+  }
+
+  void initializeFloatQueue() {
+    floatPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
+  }
+
+  void initializeDoubleQueue() {
+    doublePQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
+  }
+
+  void initializeStringQueue() {
+    stringPQ = new PriorityQueue<>(k, Comparator.comparing(o -> o.right));
   }
 
   @Override


(iotdb) 01/14: multiple refactors

Posted by ji...@apache.org.
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 93a97ed6e95f4836f151551683d3fc1667b578b4
Author: Tian Jiang <jt...@163.com>
AuthorDate: Tue Aug 25 20:50:15 2026 +0800

    multiple refactors
---
 .../plan/relational/type/InternalTypeManager.java  | 67 ++++------------------
 .../relational/type/InternalTypeManagerTest.java   | 61 ++++++++++++++++++++
 2 files changed, 72 insertions(+), 56 deletions(-)

diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManager.java
index c3d2fcd880f..8d236c888fa 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManager.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManager.java
@@ -82,66 +82,21 @@ public class InternalTypeManager implements TypeManager {
     if (type == null) {
       return null;
     }
-    TypeEnum typeEnum = type.getTypeEnum();
-    switch (typeEnum) {
-      case TEXT:
-        return TSDataType.TEXT;
-      case FLOAT:
-        return TSDataType.FLOAT;
-      case DOUBLE:
-        return TSDataType.DOUBLE;
-      case INT32:
-        return TSDataType.INT32;
-      case INT64:
-        return TSDataType.INT64;
-      case BOOLEAN:
-        return TSDataType.BOOLEAN;
-      case UNKNOWN:
-        return TSDataType.UNKNOWN;
-      case DATE:
-        return TSDataType.DATE;
-      case TIMESTAMP:
-        return TSDataType.TIMESTAMP;
-      case BLOB:
-      case ROW:
-        return TSDataType.BLOB;
-      case STRING:
-        return TSDataType.STRING;
-      case OBJECT:
-        return TSDataType.OBJECT;
-      default:
-        throw new IllegalArgumentException();
+    final TypeEnum typeEnum = type.getTypeEnum();
+    // TSDataType has no ROW counterpart, so preserve its existing binary representation.
+    if (typeEnum == TypeEnum.ROW) {
+      return TSDataType.BLOB;
     }
+    if (typeEnum == TypeEnum.VECTOR) {
+      throw new IllegalArgumentException();
+    }
+    return TSDataType.valueOf(typeEnum.name());
   }
 
   public static Type fromTSDataType(TSDataType dataType) {
-    switch (dataType) {
-      case TEXT:
-        return TEXT;
-      case FLOAT:
-        return FLOAT;
-      case DOUBLE:
-        return DOUBLE;
-      case INT32:
-        return INT32;
-      case INT64:
-        return INT64;
-      case BOOLEAN:
-        return BOOLEAN;
-      case UNKNOWN:
-        return UNKNOWN;
-      case DATE:
-        return DATE;
-      case TIMESTAMP:
-        return TIMESTAMP;
-      case BLOB:
-        return BLOB;
-      case OBJECT:
-        return OBJECT;
-      case STRING:
-        return STRING;
-      default:
-        throw new IllegalArgumentException();
+    if (dataType == TSDataType.VECTOR) {
+      throw new IllegalArgumentException();
     }
+    return Type.fromTsDataType(dataType);
   }
 }
diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManagerTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManagerTest.java
new file mode 100644
index 00000000000..f579d0d0f01
--- /dev/null
+++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/type/InternalTypeManagerTest.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.iotdb.commons.queryengine.plan.relational.type;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.RowType;
+import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.read.common.type.VectorType;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+public class InternalTypeManagerTest {
+
+  @Test
+  public void testScalarTypeConversion() {
+    for (final TSDataType dataType : TSDataType.values()) {
+      if (dataType == TSDataType.VECTOR) {
+        continue;
+      }
+
+      final Type type = InternalTypeManager.fromTSDataType(dataType);
+      assertSame(Type.fromTsDataType(dataType), type);
+      assertEquals(dataType, InternalTypeManager.getTSDataType(type));
+    }
+  }
+
+  @Test
+  public void testSpecialTypes() {
+    assertNull(InternalTypeManager.getTSDataType(null));
+    assertEquals(
+        TSDataType.BLOB,
+        InternalTypeManager.getTSDataType(
+            RowType.anonymousRow(Type.fromTsDataType(TSDataType.INT32))));
+
+    Assert.assertThrows(
+        IllegalArgumentException.class,
+        () -> InternalTypeManager.fromTSDataType(TSDataType.VECTOR));
+    Assert.assertThrows(
+        IllegalArgumentException.class, () -> InternalTypeManager.getTSDataType(VectorType.VECTOR));
+  }
+}


(iotdb) 14/14: multiple refactors

Posted by ji...@apache.org.
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 64e1313f5b4f1e49186a6da87f8312bc426c60e4
Author: Tian Jiang <jt...@163.com>
AuthorDate: Mon Sep 7 15:23:15 2026 +0800

    multiple refactors
---
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |    3 +
 .../org/apache/iotdb/calc/i18n/CalcMessages.java   |    3 +
 .../AbstractApproxPercentileAccumulator.java       |   28 +-
 .../relational/aggregation/AccumulatorFactory.java |   60 +-
 .../relational/aggregation/ExtremeAccumulator.java |   44 +-
 .../relational/aggregation/FirstAccumulator.java   |   61 +-
 .../relational/aggregation/FirstByAccumulator.java |   45 +-
 .../relational/aggregation/LastAccumulator.java    |   60 +-
 .../relational/aggregation/LastByAccumulator.java  |   38 +-
 .../relational/aggregation/MaxAccumulator.java     |   48 +-
 .../relational/aggregation/MinAccumulator.java     |   48 +-
 ...AbstractGroupedApproxPercentileAccumulator.java |   27 +-
 .../grouped/GroupedExtremeAccumulator.java         |  329 +--
 .../grouped/GroupedFirstAccumulator.java           |  443 ++--
 .../grouped/GroupedFirstByAccumulator.java         |  445 ++--
 .../grouped/GroupedLastAccumulator.java            |  442 ++--
 .../grouped/GroupedLastByAccumulator.java          |  445 ++--
 .../aggregation/grouped/GroupedMaxAccumulator.java |  350 +--
 .../grouped/GroupedMaxMinByBaseAccumulator.java    |  668 ++----
 .../aggregation/grouped/GroupedMinAccumulator.java |  478 +---
 .../relational/ColumnTransformerBuilder.java       |  122 +-
 .../calc/plan/planner/TableOperatorGenerator.java  |  102 +-
 .../dag/column/unary/InColumnTransformer.java      |  220 +-
 .../AbstractCastFunctionColumnTransformer.java     |  353 +--
 .../scalar/CastFunctionColumnTransformer.java      |   42 +-
 .../unary/scalar/FormatColumnTransformer.java      |   37 +-
 .../unary/scalar/RoundColumnTransformer.java       |   36 +-
 .../scalar/RoundFunctionColumnTransformer.java     |   37 +-
 .../scalar/TryCastFunctionColumnTransformer.java   |   43 +-
 .../transformation/dag/util/CastFunctionUtils.java |  246 +-
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 2370 +++++++++++++++++++-
 .../apache/iotdb/calc/utils/TypeServicesTest.java  |   84 +
 32 files changed, 3691 insertions(+), 4066 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 36667b2596a..c16e60e0f6b 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
@@ -28,6 +28,7 @@ public final class CalcMessages {
   public static final String ARRAYS_NOT_SAME_LENGTH = "Arrays not same length";
   public static final String CANNOT_ADD_NAN_TO_T_DIGEST = "Cannot add NaN to t-digest";
   public static final String CANNOT_CAST_TO_BOOLEAN = "\"%s\" cannot be cast to [BOOLEAN]";
+  public static final String CANNOT_CAST_TO_TYPE = "\"%s\" cannot be cast to [%s]";
   public static final String CANNOT_PARSE_STRING_TO_DOUBLE = "Cannot parse String to double: ";
   public static final String CANT_HAPPEN_LOOP_FELL_THROUGH =
       "Can't happen ... loop fell through";
@@ -73,6 +74,8 @@ public final class CalcMessages {
   public static final String INITIAL_CAPACITY_EXCEEDS_LIMIT = "Initial capacity (%d) exceeds %d";
   public static final String INPUT_ROW_UTILS_SHOULD_NOT_BE_INSTANTIATED =
       "InputRowUtils should not be instantiated.";
+  public static final String IN_LIST_LITERAL_FOR_TIMESTAMP_TYPE_RESTRICTION =
+      "InList Literal for TIMESTAMP can only be LongLiteral, DoubleLiteral and GenericLiteral, current is %s";
   public static final String INVALID_AGGREGATION_FUNCTION = "Invalid Aggregation function: ";
   public static final String INVALID_TEXT_INPUT_FOR_BOOLEAN =
       "Invalid text input for boolean type: %s";
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 133971f7460..0045fe8ff79 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
@@ -28,6 +28,7 @@ public final class CalcMessages {
   public static final String ARRAYS_NOT_SAME_LENGTH = "数组长度不一致";
   public static final String CANNOT_ADD_NAN_TO_T_DIGEST = "不能将 NaN 添加到 t-digest";
   public static final String CANNOT_CAST_TO_BOOLEAN = "\"%s\" 无法转换为 [BOOLEAN]";
+  public static final String CANNOT_CAST_TO_TYPE = "\"%s\" 无法转换为 [%s]";
   public static final String CANNOT_PARSE_STRING_TO_DOUBLE = "无法将字符串解析为 double:";
   public static final String CANT_HAPPEN_LOOP_FELL_THROUGH = "不应发生……循环穿透";
   public static final String COUNT_ALL_ACCUMULATOR_DOES_NOT_SUPPORT_STATISTICS =
@@ -71,6 +72,8 @@ public final class CalcMessages {
   public static final String INITIAL_CAPACITY_EXCEEDS_LIMIT = "初始容量 (%d) 超过 %d";
   public static final String INPUT_ROW_UTILS_SHOULD_NOT_BE_INSTANTIATED =
       "InputRowUtils 不应被实例化。";
+  public static final String IN_LIST_LITERAL_FOR_TIMESTAMP_TYPE_RESTRICTION =
+      "TIMESTAMP 的 InList 字面量只能是 LongLiteral、DoubleLiteral 和 GenericLiteral,当前为 %s";
   public static final String INVALID_AGGREGATION_FUNCTION = "无效的聚合函数:";
   public static final String INVALID_TEXT_INPUT_FOR_BOOLEAN =
       "布尔类型的文本输入无效:%s";
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
index a23895de741..69804e03b64 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AbstractApproxPercentileAccumulator.java
@@ -32,9 +32,9 @@ 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.ReadWriteIOUtils;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.nio.ByteBuffer;
+import java.util.function.BiConsumer;
 
 public abstract class AbstractApproxPercentileAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
@@ -43,12 +43,17 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
   protected final TDigest tDigest = new TDigest();
   protected final TSDataType seriesDataType;
   private final TypeServices.NumericResultWriter resultWriter;
+  private final BiConsumer<Column[], AggregationMask> inputAdder;
   protected double percentage;
 
   AbstractApproxPercentileAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
     this.resultWriter =
         TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(Type.fromTsDataType(seriesDataType));
+    this.inputAdder =
+        TypeServices.APPROX_PERCENTILE_INPUT_SERVICE
+            .call(Type.fromTsDataType(seriesDataType))
+            .apply(this);
   }
 
   @Override
@@ -72,26 +77,7 @@ public abstract class AbstractApproxPercentileAccumulator implements TableAccumu
           String.format(
               CalcMessages.APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS, arguments.length));
     }
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(arguments, mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(arguments, mask);
-        return;
-      case FLOAT:
-        addFloatInput(arguments, mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(arguments, mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
-                seriesDataType));
-    }
+    inputAdder.accept(arguments, mask);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
index 342ae536a8e..11c3315ab2a 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java
@@ -23,11 +23,6 @@ import org.apache.iotdb.calc.execution.aggregation.CentralMomentAccumulator;
 import org.apache.iotdb.calc.execution.aggregation.CovarianceAccumulator;
 import org.apache.iotdb.calc.execution.aggregation.RegressionAccumulator;
 import org.apache.iotdb.calc.execution.aggregation.VarianceAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BinaryGroupedApproxMostFrequentAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BlobGroupedApproxMostFrequentAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BooleanGroupedApproxMostFrequentAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.DoubleGroupedApproxMostFrequentAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.FloatGroupedApproxMostFrequentAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedApproxCountDistinctAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedApproxPercentileAccumulator;
@@ -54,10 +49,9 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedSumAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedUserDefinedAggregateAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedVarianceAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.IntGroupedApproxMostFrequentAccumulator;
-import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.LongGroupedApproxMostFrequentAccumulator;
 import org.apache.iotdb.calc.i18n.CalcMessages;
 import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.common.rpc.thrift.TAggregationType;
 import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory;
 import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.MarkDistinctHash;
@@ -77,7 +71,6 @@ import org.apache.tsfile.file.metadata.statistics.Statistics;
 import org.apache.tsfile.read.common.block.column.IntColumn;
 import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.read.common.type.TypeFactory;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import javax.annotation.Nullable;
 
@@ -506,57 +499,12 @@ public class AccumulatorFactory {
   }
 
   public static GroupedAccumulator getGroupedApproxMostFrequentAccumulator(TSDataType type) {
-    switch (type) {
-      case BOOLEAN:
-        return new BooleanGroupedApproxMostFrequentAccumulator();
-      case INT32:
-      case DATE:
-        return new IntGroupedApproxMostFrequentAccumulator();
-      case INT64:
-      case TIMESTAMP:
-        return new LongGroupedApproxMostFrequentAccumulator();
-      case FLOAT:
-        return new FloatGroupedApproxMostFrequentAccumulator();
-      case DOUBLE:
-        return new DoubleGroupedApproxMostFrequentAccumulator();
-      case TEXT:
-      case STRING:
-        return new BinaryGroupedApproxMostFrequentAccumulator();
-      case BLOB:
-        return new BlobGroupedApproxMostFrequentAccumulator();
-      case OBJECT:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION, type));
-    }
+    return TypeServices.GROUPED_APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE.call(
+        Type.fromTsDataType(type));
   }
 
   public static TableAccumulator getApproxMostFrequentAccumulator(TSDataType type) {
-    switch (type) {
-      case BOOLEAN:
-        return new BooleanApproxMostFrequentAccumulator();
-      case INT32:
-      case DATE:
-        return new IntApproxMostFrequentAccumulator();
-      case INT64:
-      case TIMESTAMP:
-        return new LongApproxMostFrequentAccumulator();
-      case FLOAT:
-        return new FloatApproxMostFrequentAccumulator();
-      case DOUBLE:
-        return new DoubleApproxMostFrequentAccumulator();
-      case TEXT:
-      case STRING:
-        return new BinaryApproxMostFrequentAccumulator();
-      case BLOB:
-        return new BlobApproxMostFrequentAccumulator();
-      case OBJECT:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION, type));
-    }
+    return TypeServices.APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE.call(Type.fromTsDataType(type));
   }
 
   public static boolean isMultiInputAggregation(TAggregationType aggregationType) {
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
index 8b97c1849e1..cbcdecf94f1 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ExtremeAccumulator.java
@@ -31,6 +31,8 @@ import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
+import java.util.function.Supplier;
+
 public class ExtremeAccumulator implements TableAccumulator {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(ExtremeAccumulator.class);
@@ -38,6 +40,7 @@ public class ExtremeAccumulator implements TableAccumulator {
   private final Type type;
   private final TsPrimitiveType extremeResult;
   private final TypeServices.ColumnValueUpdater valueUpdater;
+  private final TypeServices.StatisticsValueUpdater statisticsValueUpdater;
   private boolean initResult;
 
   public ExtremeAccumulator(TSDataType seriesDataType) {
@@ -45,6 +48,7 @@ public class ExtremeAccumulator implements TableAccumulator {
     this.type = Type.fromTsDataType(seriesDataType);
     this.extremeResult = type.getTsPrimitiveType();
     this.valueUpdater = TypeServices.EXTREME_COLUMN_VALUE_UPDATER_SERVICE.call(type);
+    this.statisticsValueUpdater = TypeServices.EXTREME_STATISTICS_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -95,34 +99,18 @@ public class ExtremeAccumulator implements TableAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-        updateIntResult(((Number) statistics[0].getMaxValue()).intValue());
-        updateIntResult(((Number) statistics[0].getMinValue()).intValue());
-        break;
-      case INT64:
-        updateLongResult(((Number) statistics[0].getMaxValue()).longValue());
-        updateLongResult(((Number) statistics[0].getMinValue()).longValue());
-        break;
-      case FLOAT:
-        updateFloatResult(((Number) statistics[0].getMaxValue()).floatValue());
-        updateFloatResult(((Number) statistics[0].getMinValue()).floatValue());
-        break;
-      case DOUBLE:
-        updateDoubleResult(((Number) statistics[0].getMaxValue()).doubleValue());
-        updateDoubleResult(((Number) statistics[0].getMinValue()).doubleValue());
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
+    Supplier<RuntimeException> exceptionSupplier =
+        () ->
+            new UnSupportedDataTypeException(
+                String.format(
+                    CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
+    if (statisticsValueUpdater.update(
+        extremeResult, statistics[0].getMaxValue(), initResult, exceptionSupplier)) {
+      initResult = true;
+    }
+    if (statisticsValueUpdater.update(
+        extremeResult, statistics[0].getMinValue(), initResult, exceptionSupplier)) {
+      initResult = true;
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
index 183c83526db..8b67e1a1fa0 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java
@@ -49,6 +49,7 @@ public class FirstAccumulator implements TableAccumulator {
   protected final TSDataType seriesDataType;
   protected final Type type;
   private final TypeServices.PrimitiveColumnValueSetter valueSetter;
+  private final TypeServices.StatisticsValueSetter statisticsValueSetter;
   protected TsPrimitiveType firstValue;
   protected long minTime = Long.MAX_VALUE;
   protected boolean initResult = false;
@@ -60,6 +61,7 @@ public class FirstAccumulator implements TableAccumulator {
     this.type = Type.fromTsDataType(seriesDataType);
     this.firstValue = type.getTsPrimitiveType();
     this.valueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(type);
+    this.statisticsValueSetter = TypeServices.STATISTICS_VALUE_SETTER_SERVICE.call(type);
     this.canFinishAfterInit = canFinishAfterInit;
   }
 
@@ -169,53 +171,18 @@ public class FirstAccumulator implements TableAccumulator {
     if (statistics == null || statistics[0] == null) {
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        updateIntFirstValue(
-            ((Number) statistics[0].getFirstValue()).intValue(), statistics[0].getStartTime());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        updateLongFirstValue(
-            ((Number) statistics[0].getFirstValue()).longValue(), statistics[0].getStartTime());
-        break;
-      case FLOAT:
-        updateFloatFirstValue(
-            ((Number) statistics[0].getFirstValue()).floatValue(), statistics[0].getStartTime());
-        break;
-      case DOUBLE:
-        updateDoubleFirstValue(
-            ((Number) statistics[0].getFirstValue()).doubleValue(), statistics[0].getStartTime());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-      case OBJECT:
-        if (statistics[0] instanceof DateStatistics) {
-          updateBinaryFirstValue(
-              new Binary(
-                  TSDataType.getDateStringValue((Integer) statistics[0].getFirstValue()),
-                  StandardCharsets.UTF_8),
-              statistics[0].getStartTime());
-        } else {
-          if (statistics[0].getFirstValue() instanceof Binary) {
-            updateBinaryFirstValue(
-                (Binary) statistics[0].getFirstValue(), statistics[0].getStartTime());
-          } else {
-            updateBinaryFirstValue(
-                new Binary(String.valueOf(statistics[0].getFirstValue()), StandardCharsets.UTF_8),
-                statistics[0].getStartTime());
-          }
-        }
-        break;
-      case BOOLEAN:
-        updateBooleanFirstValue(
-            (boolean) statistics[0].getFirstValue(), statistics[0].getStartTime());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
+    Object value = statistics[0].getFirstValue();
+    if (statistics[0] instanceof DateStatistics) {
+      value = new Binary(TSDataType.getDateStringValue((Integer) value), StandardCharsets.UTF_8);
+    }
+    if (checkAndUpdateFirstTime(statistics[0].getStartTime())) {
+      statisticsValueSetter.set(
+          firstValue,
+          value,
+          () ->
+              new UnSupportedDataTypeException(
+                  String.format(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType)));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
index 496485cb684..f9110d5665c 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java
@@ -37,7 +37,6 @@ import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull;
@@ -57,6 +56,7 @@ public class FirstByAccumulator implements TableAccumulator {
 
   private final Type xType;
   private final TypeServices.PrimitiveColumnValueSetter xValueSetter;
+  private final TypeServices.StatisticsValueSetter xStatisticsValueSetter;
   private final TsPrimitiveType xResult;
   private boolean xIsNull = true;
 
@@ -78,6 +78,7 @@ public class FirstByAccumulator implements TableAccumulator {
 
     this.xType = Type.fromTsDataType(xDataType);
     this.xValueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(xType);
+    this.xStatisticsValueSetter = TypeServices.STATISTICS_VALUE_SETTER_SERVICE.call(xType);
     this.xResult = xType.getTsPrimitiveType();
     this.canFinishAfterInit = canFinishAfterInit;
   }
@@ -195,41 +196,13 @@ public class FirstByAccumulator implements TableAccumulator {
           yFirstTime = yStatistics.getStartTime();
           xIsNull = false;
 
-          switch (xDataType) {
-            case INT32:
-            case DATE:
-              xResult.setInt(((Number) xStatistics.getFirstValue()).intValue());
-              break;
-            case INT64:
-            case TIMESTAMP:
-              xResult.setLong(((Number) xStatistics.getFirstValue()).longValue());
-              break;
-            case FLOAT:
-              xResult.setFloat(((Number) statistics[0].getFirstValue()).floatValue());
-              break;
-            case DOUBLE:
-              xResult.setDouble(((Number) statistics[0].getFirstValue()).doubleValue());
-              break;
-            case TEXT:
-            case BLOB:
-            case OBJECT:
-            case STRING:
-              if (statistics[0].getFirstValue() instanceof Binary) {
-                xResult.setBinary((Binary) statistics[0].getFirstValue());
-              } else {
-                xResult.setBinary(
-                    new Binary(
-                        String.valueOf(statistics[0].getFirstValue()), StandardCharsets.UTF_8));
-              }
-              break;
-            case BOOLEAN:
-              xResult.setBoolean((boolean) statistics[0].getFirstValue());
-              break;
-            default:
-              throw new UnSupportedDataTypeException(
-                  String.format(
-                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, yDataType));
-          }
+          xStatisticsValueSetter.set(
+              xResult,
+              xStatistics.getFirstValue(),
+              () ->
+                  new UnSupportedDataTypeException(
+                      String.format(
+                          CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, yDataType)));
         }
       }
     } else {
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
index 8bad5c9bc0a..1482e6837c9 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java
@@ -49,6 +49,7 @@ public class LastAccumulator implements TableAccumulator, TypeServices.LastValue
   protected final TSDataType seriesDataType;
   protected final Type type;
   private final TypeServices.PrimitiveColumnValueSetter valueSetter;
+  private final TypeServices.StatisticsValueSetter statisticsValueSetter;
   private final TypeServices.LastValueDeserializer valueDeserializer;
   protected TsPrimitiveType lastValue;
   protected long maxTime = Long.MIN_VALUE;
@@ -60,6 +61,7 @@ public class LastAccumulator implements TableAccumulator, TypeServices.LastValue
     this.type = Type.fromTsDataType(seriesDataType);
     this.lastValue = type.getTsPrimitiveType();
     this.valueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(type);
+    this.statisticsValueSetter = TypeServices.STATISTICS_VALUE_SETTER_SERVICE.call(type);
     this.valueDeserializer = TypeServices.LAST_VALUE_DESERIALIZER_SERVICE.call(type);
   }
 
@@ -227,52 +229,18 @@ public class LastAccumulator implements TableAccumulator, TypeServices.LastValue
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        updateIntLastValue(
-            ((Number) statistics[0].getLastValue()).intValue(), statistics[0].getEndTime());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        updateLongLastValue(
-            ((Number) statistics[0].getLastValue()).longValue(), statistics[0].getEndTime());
-        break;
-      case FLOAT:
-        updateFloatLastValue(
-            ((Number) statistics[0].getLastValue()).floatValue(), statistics[0].getEndTime());
-        break;
-      case DOUBLE:
-        updateDoubleLastValue(
-            ((Number) statistics[0].getLastValue()).doubleValue(), statistics[0].getEndTime());
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        if (statistics[0] instanceof DateStatistics) {
-          updateBinaryLastValue(
-              new Binary(
-                  TSDataType.getDateStringValue((Integer) statistics[0].getLastValue()),
-                  StandardCharsets.UTF_8),
-              statistics[0].getEndTime());
-        } else {
-          if (statistics[0].getLastValue() instanceof Binary) {
-            updateBinaryLastValue(
-                (Binary) statistics[0].getLastValue(), statistics[0].getEndTime());
-          } else {
-            updateBinaryLastValue(
-                new Binary(String.valueOf(statistics[0].getLastValue()), StandardCharsets.UTF_8),
-                statistics[0].getEndTime());
-          }
-        }
-        break;
-      case BOOLEAN:
-        updateBooleanLastValue((boolean) statistics[0].getLastValue(), statistics[0].getEndTime());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
+    Object value = statistics[0].getLastValue();
+    if (statistics[0] instanceof DateStatistics) {
+      value = new Binary(TSDataType.getDateStringValue((Integer) value), StandardCharsets.UTF_8);
+    }
+    if (checkAndUpdateLastTime(statistics[0].getEndTime())) {
+      statisticsValueSetter.set(
+          lastValue,
+          value,
+          () ->
+              new UnSupportedDataTypeException(
+                  String.format(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType)));
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
index e466bbea9be..131f5bba43f 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java
@@ -56,6 +56,7 @@ public class LastByAccumulator implements TableAccumulator {
 
   private final Type xType;
   private final TypeServices.PrimitiveColumnValueSetter xValueSetter;
+  private final TypeServices.StatisticsValueSetter xStatisticsValueSetter;
   private final TsPrimitiveType xResult;
   private boolean xIsNull = true;
 
@@ -70,6 +71,7 @@ public class LastByAccumulator implements TableAccumulator {
     this.yIsTimeColumn = yIsTimeColumn;
     this.xType = Type.fromTsDataType(xDataType);
     this.xValueSetter = TypeServices.PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.call(xType);
+    this.xStatisticsValueSetter = TypeServices.STATISTICS_VALUE_SETTER_SERVICE.call(xType);
     this.xResult = xType.getTsPrimitiveType();
   }
 
@@ -201,35 +203,13 @@ public class LastByAccumulator implements TableAccumulator {
           yLastTime = yStatistics.getEndTime();
           xIsNull = false;
 
-          switch (xDataType) {
-            case INT32:
-            case DATE:
-              xResult.setInt(((Number) xStatistics.getLastValue()).intValue());
-              break;
-            case INT64:
-            case TIMESTAMP:
-              xResult.setLong(((Number) xStatistics.getLastValue()).longValue());
-              break;
-            case FLOAT:
-              xResult.setFloat(((Number) statistics[0].getLastValue()).floatValue());
-              break;
-            case DOUBLE:
-              xResult.setDouble(((Number) statistics[0].getLastValue()).doubleValue());
-              break;
-            case TEXT:
-            case BLOB:
-            case STRING:
-            case OBJECT:
-              xResult.setBinary((Binary) statistics[0].getLastValue());
-              break;
-            case BOOLEAN:
-              xResult.setBoolean((boolean) statistics[0].getLastValue());
-              break;
-            default:
-              throw new UnSupportedDataTypeException(
-                  String.format(
-                      CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, yDataType));
-          }
+          xStatisticsValueSetter.set(
+              xResult,
+              xStatistics.getLastValue(),
+              () ->
+                  new UnSupportedDataTypeException(
+                      String.format(
+                          CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, yDataType)));
         }
       }
     } else {
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
index 887916bcb48..edc6b970239 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java
@@ -25,7 +25,6 @@ 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.BinaryStatistics;
 import org.apache.tsfile.file.metadata.statistics.Statistics;
 import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
@@ -33,7 +32,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.function.Supplier;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -43,6 +42,7 @@ public class MaxAccumulator implements TableAccumulator {
   private final TSDataType seriesDataType;
   private final Type type;
   private final TypeServices.ColumnValueUpdater valueUpdater;
+  private final TypeServices.StatisticsValueUpdater statisticsValueUpdater;
   private final TsPrimitiveType maxResult;
   private boolean initResult;
 
@@ -51,6 +51,7 @@ public class MaxAccumulator implements TableAccumulator {
     this.type = Type.fromTsDataType(seriesDataType);
     this.maxResult = type.getTsPrimitiveType();
     this.valueUpdater = TypeServices.MAX_COLUMN_VALUE_UPDATER_SERVICE.call(type);
+    this.statisticsValueUpdater = TypeServices.MAX_STATISTICS_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -112,41 +113,14 @@ public class MaxAccumulator implements TableAccumulator {
     if (statistics == null || statistics[0] == null) {
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        updateIntMaxValue(((Number) statistics[0].getMaxValue()).intValue());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        updateLongMaxValue(((Number) statistics[0].getMaxValue()).longValue());
-        break;
-      case FLOAT:
-        updateFloatMaxValue(((Number) statistics[0].getMaxValue()).floatValue());
-        break;
-      case DOUBLE:
-        updateDoubleMaxValue(((Number) statistics[0].getMaxValue()).doubleValue());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-        if (statistics[0] instanceof BinaryStatistics) {
-          updateBinaryMaxValue((Binary) statistics[0].getMaxValue());
-        } else {
-          if (statistics[0].getMaxValue() instanceof Binary) {
-            updateBinaryMaxValue((Binary) statistics[0].getMaxValue());
-          } else {
-            updateBinaryMaxValue(
-                new Binary(String.valueOf(statistics[0].getMaxValue()), StandardCharsets.UTF_8));
-          }
-        }
-        break;
-      case BOOLEAN:
-        updateBooleanMaxValue((boolean) statistics[0].getMaxValue());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
+    Supplier<RuntimeException> exceptionSupplier =
+        () ->
+            new UnSupportedDataTypeException(
+                String.format(
+                    CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
+    if (statisticsValueUpdater.update(
+        maxResult, statistics[0].getMaxValue(), initResult, exceptionSupplier)) {
+      initResult = true;
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
index 5002d72c713..cedcb291037 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java
@@ -25,7 +25,6 @@ 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.BinaryStatistics;
 import org.apache.tsfile.file.metadata.statistics.Statistics;
 import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
@@ -33,7 +32,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.function.Supplier;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -43,6 +42,7 @@ public class MinAccumulator implements TableAccumulator {
   private final TSDataType seriesDataType;
   private final Type type;
   private final TypeServices.ColumnValueUpdater valueUpdater;
+  private final TypeServices.StatisticsValueUpdater statisticsValueUpdater;
   private final TsPrimitiveType minResult;
   private boolean initResult;
 
@@ -51,6 +51,7 @@ public class MinAccumulator implements TableAccumulator {
     this.type = Type.fromTsDataType(seriesDataType);
     this.minResult = type.getTsPrimitiveType();
     this.valueUpdater = TypeServices.MIN_COLUMN_VALUE_UPDATER_SERVICE.call(type);
+    this.statisticsValueUpdater = TypeServices.MIN_STATISTICS_VALUE_UPDATER_SERVICE.call(type);
   }
 
   @Override
@@ -112,41 +113,14 @@ public class MinAccumulator implements TableAccumulator {
     if (statistics == null || statistics[0] == null) {
       return;
     }
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        updateIntMinValue(((Number) statistics[0].getMinValue()).intValue());
-        break;
-      case INT64:
-      case TIMESTAMP:
-        updateLongMinValue(((Number) statistics[0].getMinValue()).longValue());
-        break;
-      case FLOAT:
-        updateFloatMinValue(((Number) statistics[0].getMinValue()).floatValue());
-        break;
-      case DOUBLE:
-        updateDoubleMinValue(((Number) statistics[0].getMinValue()).doubleValue());
-        break;
-      case TEXT:
-      case BLOB:
-      case STRING:
-        if (statistics[0] instanceof BinaryStatistics) {
-          updateBinaryMinValue((Binary) statistics[0].getMinValue());
-        } else {
-          if (statistics[0].getMinValue() instanceof Binary) {
-            updateBinaryMinValue((Binary) statistics[0].getMinValue());
-          } else {
-            updateBinaryMinValue(
-                new Binary(String.valueOf(statistics[0].getMinValue()), StandardCharsets.UTF_8));
-          }
-        }
-        break;
-      case BOOLEAN:
-        updateBooleanMinValue((boolean) statistics[0].getMinValue());
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
+    Supplier<RuntimeException> exceptionSupplier =
+        () ->
+            new UnSupportedDataTypeException(
+                String.format(
+                    CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
+    if (statisticsValueUpdater.update(
+        minResult, statistics[0].getMinValue(), initResult, exceptionSupplier)) {
+      initResult = true;
     }
   }
 
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
index 7f6834f0004..fd637beb1f5 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/AbstractGroupedApproxPercentileAccumulator.java
@@ -32,7 +32,6 @@ 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.ReadWriteIOUtils;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.nio.ByteBuffer;
 
@@ -42,6 +41,7 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
       RamUsageEstimator.shallowSizeOfInstance(GroupedApproxPercentileAccumulator.class);
   protected final TSDataType seriesDataType;
   private final TypeServices.NumericResultWriter resultWriter;
+  private final TypeServices.TriConsumer<int[], Column[], AggregationMask> inputAdder;
   protected double percentage;
   protected final TDigestBigArray array = new TDigestBigArray();
 
@@ -49,6 +49,10 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
     this.seriesDataType = seriesDataType;
     this.resultWriter =
         TypeServices.NUMERIC_RESULT_WRITER_SERVICE.call(Type.fromTsDataType(seriesDataType));
+    this.inputAdder =
+        TypeServices.GROUPED_APPROX_PERCENTILE_INPUT_SERVICE
+            .call(Type.fromTsDataType(seriesDataType))
+            .apply(this);
   }
 
   @Override
@@ -73,26 +77,7 @@ public abstract class AbstractGroupedApproxPercentileAccumulator implements Grou
               CalcMessages.APPROX_PERCENTILE_REQUIRES_TWO_OR_THREE_ARGUMENTS, arguments.length));
     }
 
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(groupIds, arguments, mask);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments, mask);
-        break;
-      case FLOAT:
-        addFloatInput(groupIds, arguments, mask);
-        break;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments, mask);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
-                seriesDataType));
-    }
+    inputAdder.accept(groupIds, arguments, mask);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
index 9735d49066e..0e2a746e649 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedExtremeAccumulator.java
@@ -20,20 +20,27 @@
 package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped;
 
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BinaryBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BooleanBigArray;
 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.FloatBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
 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.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
-public class GroupedExtremeAccumulator implements GroupedAccumulator {
+public class GroupedExtremeAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedInputConsumer {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedExtremeAccumulator.class);
   private final TSDataType seriesDataType;
@@ -44,164 +51,38 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
   private IntBigArray intValues;
   private FloatBigArray floatValues;
   private DoubleBigArray doubleValues;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedInputReader inputReader;
 
   public GroupedExtremeAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case INT32:
-        intValues = new IntBigArray();
-        return;
-      case INT64:
-        longValues = new LongBigArray();
-        return;
-      case FLOAT:
-        floatValues = new FloatBigArray();
-        return;
-      case DOUBLE:
-        doubleValues = new DoubleBigArray();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-    }
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueService = TypeServices.GROUPED_EXTREME_VALUE_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_EXTREME_INPUT_READER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += intValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += longValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += floatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += doubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        break;
-      case BOOLEAN:
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-    }
-
-    return INSTANCE_SIZE + valuesSize;
+    return INSTANCE_SIZE + valueService.sizeOf(this);
   }
 
   @Override
   public void setGroupCount(long groupCount) {
     inits.ensureCapacity(groupCount);
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        floatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        doubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
 
-    switch (seriesDataType) {
-      case INT32:
-        addIntInput(groupIds, arguments[0], mask);
-        return;
-      case INT64:
-        addLongInput(groupIds, arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
   public void addIntermediate(int[] groupIds, Column argument) {
 
-    for (int i = 0; i < groupIds.length; i++) {
-      if (argument.isNull(i)) {
-        continue;
-      }
-
-      switch (seriesDataType) {
-        case INT32:
-          updateIntValue(groupIds[i], argument.getInt(i));
-          break;
-        case INT64:
-          updateLongValue(groupIds[i], argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatValue(groupIds[i], argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleValue(groupIds[i], argument.getDouble(i));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-        case BOOLEAN:
-        case DATE:
-        case TIMESTAMP:
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-      }
-    }
+    inputReader.addIntermediate(groupIds, argument, this);
   }
 
   @Override
@@ -209,31 +90,7 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-        case BOOLEAN:
-        case DATE:
-        case TIMESTAMP:
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -242,31 +99,7 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-        case BOOLEAN:
-        case DATE:
-        case TIMESTAMP:
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -276,31 +109,7 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
   @Override
   public void reset() {
     inits.reset();
-    switch (seriesDataType) {
-      case INT32:
-        intValues.reset();
-        return;
-      case INT64:
-        longValues.reset();
-        return;
-      case FLOAT:
-        floatValues.reset();
-        return;
-      case DOUBLE:
-        doubleValues.reset();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-      case BOOLEAN:
-      case DATE:
-      case TIMESTAMP:
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
-    }
+    valueService.reset(this);
   }
 
   private void addIntInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
@@ -448,4 +257,100 @@ public class GroupedExtremeAccumulator implements GroupedAccumulator {
     int absComparison = Double.compare(Math.abs(left), Math.abs(right));
     return absComparison == 0 ? Double.compare(left, right) : absComparison;
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_EXTREME_AGGREGATION, seriesDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    intValues = new IntBigArray();
+  }
+
+  @Override
+  public void initializeLongValues() {
+    longValues = new LongBigArray();
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    floatValues = new FloatBigArray();
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    doubleValues = new DoubleBigArray();
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    throw unsupportedException();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    throw unsupportedException();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return intValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return longValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return floatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return doubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    throw unsupportedException();
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    throw unsupportedException();
+  }
+
+  @Override
+  public void updateInt(int groupId, int value) {
+    updateIntValue(groupId, value);
+  }
+
+  @Override
+  public void updateLong(int groupId, long value) {
+    updateLongValue(groupId, value);
+  }
+
+  @Override
+  public void updateFloat(int groupId, float value) {
+    updateFloatValue(groupId, value);
+  }
+
+  @Override
+  public void updateDouble(int groupId, double value) {
+    updateDoubleValue(groupId, value);
+  }
+
+  @Override
+  public void updateBinary(int groupId, Binary value) {
+    throw unsupportedException();
+  }
+
+  @Override
+  public void updateBoolean(int groupId, boolean value) {
+    throw unsupportedException();
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
index 6090aea3075..68c048358eb 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
@@ -34,6 +35,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -41,15 +43,19 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.tsfile.utils.BytesUtils.boolToBytes;
-import static org.apache.tsfile.utils.BytesUtils.doubleToBytes;
-import static org.apache.tsfile.utils.BytesUtils.floatToBytes;
-import static org.apache.tsfile.utils.BytesUtils.intToBytes;
 import static org.apache.tsfile.utils.BytesUtils.longToBytes;
 
-public class GroupedFirstAccumulator implements GroupedAccumulator {
+public class GroupedFirstAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedTimeValueConsumer {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedFirstAccumulator.class);
   private final TSDataType seriesDataType;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedValueSerializer valueSerializer;
+  private final TypeServices.GroupedTimeInputReader inputReader;
+  private final TypeServices.GroupedTimeValueDeserializer valueDeserializer;
   private final LongBigArray minTimes = new LongBigArray(Long.MAX_VALUE);
   private final BooleanBigArray inits = new BooleanBigArray();
   private final BooleanBigArray initNullTimeValues = new BooleanBigArray();
@@ -63,73 +69,21 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
 
   public GroupedFirstAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues = new IntBigArray();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues = new LongBigArray();
-        return;
-      case FLOAT:
-        floatValues = new FloatBigArray();
-        return;
-      case DOUBLE:
-        doubleValues = new DoubleBigArray();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues = new BinaryBigArray();
-        return;
-      case BOOLEAN:
-        booleanValues = new BooleanBigArray();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueService = TypeServices.GROUPED_VALUE_SERVICE.call(type);
+    this.valueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_TIME_INPUT_READER_SERVICE.call(type);
+    this.valueDeserializer = TypeServices.GROUPED_TIME_VALUE_DESERIALIZER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += intValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += longValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += floatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += doubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += binaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += booleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
-
     return INSTANCE_SIZE
         + inits.sizeOf()
         + minTimes.sizeOf()
         + initNullTimeValues.sizeOf()
-        + valuesSize;
+        + valueService.sizeOf(this);
   }
 
   @Override
@@ -137,67 +91,13 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
     minTimes.ensureCapacity(groupCount);
     inits.ensureCapacity(groupCount);
     initNullTimeValues.ensureCapacity(groupCount);
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        floatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        doubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        booleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
     // arguments[0] is value column, arguments[1] is time column
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
@@ -219,68 +119,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
       boolean isOrderTimeNull = BytesUtils.bytesToBool(bytes, offset);
       offset += 1;
       int groupId = groupIds[i];
-
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          int intVal = BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntValue(groupId, intVal, time);
-          } else {
-            updateIntNullTimeValue(groupId, intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongValue(groupId, longVal, time);
-          } else {
-            updateLongNullTimeValue(groupId, longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatValue(groupId, floatVal, time);
-          } else {
-            updateFloatNullTimeValue(groupId, floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleValue(groupId, doubleVal, time);
-          } else {
-            updateDoubleNullTimeValue(groupId, doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          Binary binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          if (!isOrderTimeNull) {
-            updateBinaryValue(groupId, binaryVal, time);
-          } else {
-            updateBinaryNullTimeValue(groupId, binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = BytesUtils.bytesToBool(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateBooleanValue(groupId, boolVal, time);
-          } else {
-            updateBooleanNullTimeValue(groupId, boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-      }
+      valueDeserializer.deserialize(bytes, offset, groupId, time, isOrderTimeNull, this);
     }
   }
 
@@ -303,34 +142,7 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(intValues.get(groupId));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(longValues.get(groupId));
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(floatValues.get(groupId));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(doubleValues.get(groupId));
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(binaryValues.get(groupId));
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(booleanValues.get(groupId));
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    valueService.write(this, groupId, columnBuilder);
   }
 
   @Override
@@ -341,96 +153,17 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
     minTimes.reset();
     inits.reset();
     initNullTimeValues.reset();
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.reset();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.reset();
-        return;
-      case FLOAT:
-        floatValues.reset();
-        return;
-      case DOUBLE:
-        doubleValues.reset();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.reset();
-        return;
-      case BOOLEAN:
-        booleanValues.reset();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    valueService.reset(this);
   }
 
   private byte[] serializeTimeWithValue(int groupId) {
-    byte[] bytes;
-    int length = Long.BYTES;
+    int length = Long.BYTES + 1 + valueSerializer.calcTypeSize(this, groupId);
     boolean isOrderTimeNull = !inits.get(groupId);
-    length += 1;
-
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        length += Integer.BYTES;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        intToBytes(intValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case INT64:
-      case TIMESTAMP:
-        length += Long.BYTES;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        longToBytes(longValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case FLOAT:
-        length += Float.BYTES;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        floatToBytes(floatValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case DOUBLE:
-        length += Double.BYTES;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        doubleToBytes(doubleValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        byte[] values = binaryValues.get(groupId).getValues();
-        length += Integer.BYTES + values.length;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        BytesUtils.intToBytes(values.length, bytes, Long.BYTES + 1);
-        System.arraycopy(values, 0, bytes, length - values.length, values.length);
-        return bytes;
-      case BOOLEAN:
-        length++;
-        bytes = new byte[length];
-        longToBytes(minTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        boolToBytes(booleanValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
-    }
+    byte[] bytes = new byte[length];
+    longToBytes(minTimes.get(groupId), bytes, 0);
+    boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
+    valueSerializer.serialize(this, groupId, bytes, Long.BYTES + 1);
+    return bytes;
   }
 
   private boolean checkAndUpdateFirstTime(int groupId, long curTime) {
@@ -653,4 +386,124 @@ public class GroupedFirstAccumulator implements GroupedAccumulator {
       booleanValues.set(groupId, value);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_AGGREGATION, seriesDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    intValues = new IntBigArray();
+  }
+
+  @Override
+  public void initializeLongValues() {
+    longValues = new LongBigArray();
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    floatValues = new FloatBigArray();
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    doubleValues = new DoubleBigArray();
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    binaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    booleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return intValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return longValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return floatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return doubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return binaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return booleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, int value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateIntNullTimeValue(groupId, value);
+    } else {
+      updateIntValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateLong(int groupId, long value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateLongNullTimeValue(groupId, value);
+    } else {
+      updateLongValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateFloat(int groupId, float value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateFloatNullTimeValue(groupId, value);
+    } else {
+      updateFloatValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateDouble(int groupId, double value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateDoubleNullTimeValue(groupId, value);
+    } else {
+      updateDoubleValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateBinary(int groupId, Binary value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBinaryNullTimeValue(groupId, value);
+    } else {
+      updateBinaryValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateBoolean(int groupId, boolean value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBooleanNullTimeValue(groupId, value);
+    } else {
+      updateBooleanValue(groupId, value, time);
+    }
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
index 691e98e2d4f..73cbb8f0f74 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
@@ -34,6 +35,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -41,18 +43,22 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.tsfile.utils.BytesUtils.boolToBytes;
-import static org.apache.tsfile.utils.BytesUtils.doubleToBytes;
-import static org.apache.tsfile.utils.BytesUtils.floatToBytes;
-import static org.apache.tsfile.utils.BytesUtils.intToBytes;
 import static org.apache.tsfile.utils.BytesUtils.longToBytes;
 
-public class GroupedFirstByAccumulator implements GroupedAccumulator {
+public class GroupedFirstByAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedByValueConsumer {
 
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedFirstByAccumulator.class);
 
   private final TSDataType xDataType;
   private final TSDataType yDataType;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedValueSerializer valueSerializer;
+  private final TypeServices.GroupedByInputReader inputReader;
+  private final TypeServices.GroupedByValueDeserializer valueDeserializer;
 
   private final LongBigArray yFirstTimes = new LongBigArray(Long.MAX_VALUE);
 
@@ -71,71 +77,18 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
   public GroupedFirstByAccumulator(TSDataType xDataType, TSDataType yDataType) {
     this.xDataType = xDataType;
     this.yDataType = yDataType;
-
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues = new IntBigArray();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues = new LongBigArray();
-        break;
-      case FLOAT:
-        xFloatValues = new FloatBigArray();
-        break;
-      case DOUBLE:
-        xDoubleValues = new DoubleBigArray();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        xBinaryValues = new BinaryBigArray();
-        break;
-      case BOOLEAN:
-        xBooleanValues = new BooleanBigArray();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
+    Type type = Type.fromTsDataType(xDataType);
+    this.valueService = TypeServices.GROUPED_VALUE_SERVICE.call(type);
+    this.valueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_BY_INPUT_READER_SERVICE.call(type);
+    this.valueDeserializer = TypeServices.GROUPED_BY_VALUE_DESERIALIZER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += xIntValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += xLongValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += xFloatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += xDoubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += xBinaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += xBooleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
-
     return INSTANCE_SIZE
-        + valuesSize
+        + valueService.sizeOf(this)
         + yFirstTimes.sizeOf()
         + inits.sizeOf()
         + initNullTimeValues.sizeOf()
@@ -148,34 +101,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
     inits.ensureCapacity(groupCount);
     initNullTimeValues.ensureCapacity(groupCount);
     xNulls.ensureCapacity(groupCount);
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        xFloatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        xDoubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        xBinaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        xBooleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
@@ -187,34 +113,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
     inits.reset();
     initNullTimeValues.reset();
     xNulls.reset();
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.reset();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.reset();
-        break;
-      case FLOAT:
-        xFloatValues.reset();
-        break;
-      case DOUBLE:
-        xDoubleValues.reset();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        xBinaryValues.reset();
-        break;
-      case BOOLEAN:
-        xBooleanValues.reset();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
+    valueService.reset(this);
   }
 
   @Override
@@ -222,34 +121,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
     checkArgument(arguments.length == 3, "Length of input Column[] for LastBy/FirstBy should be 3");
 
     // arguments[0] is x column, arguments[1] is y column, arguments[2] is time column
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
@@ -274,73 +146,8 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
       offset += 1;
       int groupId = groupIds[i];
 
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          int intVal = isXValueNull ? 0 : BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntFirstValue(groupId, isXValueNull, intVal, curTime);
-          } else {
-            updateIntNullTimeValue(groupId, isXValueNull, intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal =
-              isXValueNull ? 0 : BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongFirstValue(groupId, isXValueNull, longVal, curTime);
-          } else {
-            updateLongNullTimeValue(groupId, isXValueNull, longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = isXValueNull ? 0 : BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatFirstValue(groupId, isXValueNull, floatVal, curTime);
-          } else {
-            updateFloatNullTimeValue(groupId, isXValueNull, floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = isXValueNull ? 0 : BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleFirstValue(groupId, isXValueNull, doubleVal, curTime);
-          } else {
-            updateDoubleNullTimeValue(groupId, isXValueNull, doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          Binary binaryVal = null;
-          if (!isXValueNull) {
-            int length = BytesUtils.bytesToInt(bytes, offset);
-            offset += Integer.BYTES;
-            binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          }
-          if (!isOrderTimeNull) {
-            updateBinaryFirstValue(groupId, isXValueNull, binaryVal, curTime);
-          } else {
-            updateBinaryNullTimeValue(groupId, isXValueNull, binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = false;
-          if (!isXValueNull) {
-            boolVal = BytesUtils.bytesToBool(bytes, offset);
-          }
-          if (!isOrderTimeNull) {
-            updateBooleanFirstValue(groupId, isXValueNull, boolVal, curTime);
-          } else {
-            updateBooleanNullTimeValue(groupId, isXValueNull, boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-      }
+      valueDeserializer.deserialize(
+          bytes, offset, groupId, isXValueNull, curTime, isOrderTimeNull, this);
     }
   }
 
@@ -360,7 +167,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
   /** Serializes group state: Time (8B) | xNull (1B) | OrderTimeNull (1B) | [Value] */
   private byte[] serializeTimeWithValue(int groupId) {
     boolean xNull = xNulls.get(groupId);
-    int length = Long.BYTES + 2 + (xNull ? 0 : calculateValueLength(groupId));
+    int length = Long.BYTES + 2 + (xNull ? 0 : valueSerializer.calcTypeSize(this, groupId));
     byte[] bytes = new byte[length];
     boolean isOrderTimeNull = !inits.get(groupId);
 
@@ -370,65 +177,11 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
 
     if (!xNull) {
       int valueOffset = Long.BYTES + 2;
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          intToBytes(xIntValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case INT64:
-        case TIMESTAMP:
-          longToBytes(xLongValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case FLOAT:
-          floatToBytes(xFloatValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case DOUBLE:
-          doubleToBytes(xDoubleValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          byte[] values = xBinaryValues.get(groupId).getValues();
-          intToBytes(values.length, bytes, valueOffset);
-          System.arraycopy(values, 0, bytes, length - values.length, values.length);
-          return bytes;
-        case BOOLEAN:
-          boolToBytes(xBooleanValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-      }
+      valueSerializer.serialize(this, groupId, bytes, valueOffset);
     }
     return bytes;
   }
 
-  private int calculateValueLength(int groupId) {
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        return Integer.BYTES;
-      case INT64:
-      case TIMESTAMP:
-        return Long.BYTES;
-      case FLOAT:
-        return Float.BYTES;
-      case DOUBLE:
-        return Double.BYTES;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        return Integer.BYTES + xBinaryValues.get(groupId).getValues().length;
-      case BOOLEAN:
-        return 1;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
-  }
-
   @Override
   public void evaluateFinal(int groupId, ColumnBuilder columnBuilder) {
     if (xNulls.get(groupId)) {
@@ -436,34 +189,7 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
       return;
     }
 
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xIntValues.get(groupId));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xLongValues.get(groupId));
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xFloatValues.get(groupId));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xDoubleValues.get(groupId));
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(xBinaryValues.get(groupId));
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xBooleanValues.get(groupId));
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
-    }
+    valueService.write(this, groupId, columnBuilder);
   }
 
   private boolean checkAndUpdateFirstTime(int groupId, boolean isXValueNull, long curTime) {
@@ -729,4 +455,125 @@ public class GroupedFirstByAccumulator implements GroupedAccumulator {
       xBooleanValues.set(groupId, xValue);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_FIRST_BY_AGGREGATION, xDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    xIntValues = new IntBigArray();
+  }
+
+  @Override
+  public void initializeLongValues() {
+    xLongValues = new LongBigArray();
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    xFloatValues = new FloatBigArray();
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    xDoubleValues = new DoubleBigArray();
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    xBinaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    xBooleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return xIntValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return xLongValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return xFloatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return xDoubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return xBinaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return xBooleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, boolean xNull, int value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateIntNullTimeValue(groupId, xNull, value);
+    } else {
+      updateIntFirstValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateLong(int groupId, boolean xNull, long value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateLongNullTimeValue(groupId, xNull, value);
+    } else {
+      updateLongFirstValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateFloat(int groupId, boolean xNull, float value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateFloatNullTimeValue(groupId, xNull, value);
+    } else {
+      updateFloatFirstValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateDouble(int groupId, boolean xNull, double value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateDoubleNullTimeValue(groupId, xNull, value);
+    } else {
+      updateDoubleFirstValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateBinary(int groupId, boolean xNull, Binary value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBinaryNullTimeValue(groupId, xNull, value);
+    } else {
+      updateBinaryFirstValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateBoolean(
+      int groupId, boolean xNull, boolean value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBooleanNullTimeValue(groupId, xNull, value);
+    } else {
+      updateBooleanFirstValue(groupId, xNull, value, time);
+    }
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
index 2fec637c093..7d35c317da1 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
@@ -34,6 +35,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -41,15 +43,19 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.tsfile.utils.BytesUtils.boolToBytes;
-import static org.apache.tsfile.utils.BytesUtils.doubleToBytes;
-import static org.apache.tsfile.utils.BytesUtils.floatToBytes;
-import static org.apache.tsfile.utils.BytesUtils.intToBytes;
 import static org.apache.tsfile.utils.BytesUtils.longToBytes;
 
-public class GroupedLastAccumulator implements GroupedAccumulator {
+public class GroupedLastAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedTimeValueConsumer {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedLastAccumulator.class);
   private final TSDataType seriesDataType;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedValueSerializer valueSerializer;
+  private final TypeServices.GroupedTimeInputReader inputReader;
+  private final TypeServices.GroupedTimeValueDeserializer valueDeserializer;
   private final LongBigArray maxTimes = new LongBigArray(Long.MIN_VALUE);
   private final BooleanBigArray inits = new BooleanBigArray();
   private final BooleanBigArray initNullTimeValues = new BooleanBigArray();
@@ -63,73 +69,21 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
 
   public GroupedLastAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues = new IntBigArray();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues = new LongBigArray();
-        return;
-      case FLOAT:
-        floatValues = new FloatBigArray();
-        return;
-      case DOUBLE:
-        doubleValues = new DoubleBigArray();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues = new BinaryBigArray();
-        return;
-      case BOOLEAN:
-        booleanValues = new BooleanBigArray();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueService = TypeServices.GROUPED_VALUE_SERVICE.call(type);
+    this.valueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_TIME_INPUT_READER_SERVICE.call(type);
+    this.valueDeserializer = TypeServices.GROUPED_TIME_VALUE_DESERIALIZER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += intValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += longValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += floatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += doubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += binaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += booleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
-
     return INSTANCE_SIZE
         + inits.sizeOf()
         + maxTimes.sizeOf()
         + initNullTimeValues.sizeOf()
-        + valuesSize;
+        + valueService.sizeOf(this);
   }
 
   @Override
@@ -137,67 +91,13 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
     maxTimes.ensureCapacity(groupCount);
     inits.ensureCapacity(groupCount);
     initNullTimeValues.ensureCapacity(groupCount);
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        floatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        doubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        booleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
     // arguments[0] is value column, arguments[1] is time column
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], arguments[1], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
@@ -220,67 +120,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
       offset += 1;
       int groupId = groupIds[i];
 
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          int intVal = BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntValue(groupId, intVal, time);
-          } else {
-            updateIntNullTimeValue(groupId, intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongValue(groupId, longVal, time);
-          } else {
-            updateLongNullTimeValue(groupId, longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatValue(groupId, floatVal, time);
-          } else {
-            updateFloatNullTimeValue(groupId, floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleValue(groupId, doubleVal, time);
-          } else {
-            updateDoubleNullTimeValue(groupId, doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          Binary binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          if (!isOrderTimeNull) {
-            updateBinaryValue(groupId, binaryVal, time);
-          } else {
-            updateBinaryNullTimeValue(groupId, binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = BytesUtils.bytesToBool(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateBooleanValue(groupId, boolVal, time);
-          } else {
-            updateBooleanNullTimeValue(groupId, boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-      }
+      valueDeserializer.deserialize(bytes, offset, groupId, time, isOrderTimeNull, this);
     }
   }
 
@@ -303,34 +143,7 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
       return;
     }
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(intValues.get(groupId));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(longValues.get(groupId));
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(floatValues.get(groupId));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(doubleValues.get(groupId));
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(binaryValues.get(groupId));
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(booleanValues.get(groupId));
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    valueService.write(this, groupId, columnBuilder);
   }
 
   @Override
@@ -341,96 +154,17 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
     maxTimes.reset();
     inits.reset();
     initNullTimeValues.reset();
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.reset();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.reset();
-        return;
-      case FLOAT:
-        floatValues.reset();
-        return;
-      case DOUBLE:
-        doubleValues.reset();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.reset();
-        return;
-      case BOOLEAN:
-        booleanValues.reset();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    valueService.reset(this);
   }
 
   private byte[] serializeTimeWithValue(int groupId) {
-    byte[] bytes;
-    int length = Long.BYTES;
+    int length = Long.BYTES + 1 + valueSerializer.calcTypeSize(this, groupId);
     boolean isOrderTimeNull = !inits.get(groupId);
-    length += 1;
-
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        length += Integer.BYTES;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        intToBytes(intValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case INT64:
-      case TIMESTAMP:
-        length += Long.BYTES;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        longToBytes(longValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case FLOAT:
-        length += Float.BYTES;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        floatToBytes(floatValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case DOUBLE:
-        length += Double.BYTES;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        doubleToBytes(doubleValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        byte[] values = binaryValues.get(groupId).getValues();
-        length += Integer.BYTES + values.length;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        BytesUtils.intToBytes(values.length, bytes, Long.BYTES + 1);
-        System.arraycopy(values, 0, bytes, length - values.length, values.length);
-        return bytes;
-      case BOOLEAN:
-        length++;
-        bytes = new byte[length];
-        longToBytes(maxTimes.get(groupId), bytes, 0);
-        boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
-        boolToBytes(booleanValues.get(groupId), bytes, Long.BYTES + 1);
-        return bytes;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
-    }
+    byte[] bytes = new byte[length];
+    longToBytes(maxTimes.get(groupId), bytes, 0);
+    boolToBytes(isOrderTimeNull, bytes, Long.BYTES);
+    valueSerializer.serialize(this, groupId, bytes, Long.BYTES + 1);
+    return bytes;
   }
 
   private boolean checkAndUpdateLastTime(int groupId, long curTime) {
@@ -653,4 +387,124 @@ public class GroupedLastAccumulator implements GroupedAccumulator {
       booleanValues.set(groupId, value);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_AGGREGATION, seriesDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    intValues = new IntBigArray();
+  }
+
+  @Override
+  public void initializeLongValues() {
+    longValues = new LongBigArray();
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    floatValues = new FloatBigArray();
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    doubleValues = new DoubleBigArray();
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    binaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    booleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return intValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return longValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return floatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return doubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return binaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return booleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, int value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateIntNullTimeValue(groupId, value);
+    } else {
+      updateIntValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateLong(int groupId, long value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateLongNullTimeValue(groupId, value);
+    } else {
+      updateLongValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateFloat(int groupId, float value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateFloatNullTimeValue(groupId, value);
+    } else {
+      updateFloatValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateDouble(int groupId, double value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateDoubleNullTimeValue(groupId, value);
+    } else {
+      updateDoubleValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateBinary(int groupId, Binary value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBinaryNullTimeValue(groupId, value);
+    } else {
+      updateBinaryValue(groupId, value, time);
+    }
+  }
+
+  @Override
+  public void updateBoolean(int groupId, boolean value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBooleanNullTimeValue(groupId, value);
+    } else {
+      updateBooleanValue(groupId, value, time);
+    }
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
index 62e6e995b0d..78340b5d890 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
@@ -34,6 +35,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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
@@ -41,18 +43,22 @@ import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.tsfile.utils.BytesUtils.boolToBytes;
-import static org.apache.tsfile.utils.BytesUtils.doubleToBytes;
-import static org.apache.tsfile.utils.BytesUtils.floatToBytes;
-import static org.apache.tsfile.utils.BytesUtils.intToBytes;
 import static org.apache.tsfile.utils.BytesUtils.longToBytes;
 
-public class GroupedLastByAccumulator implements GroupedAccumulator {
+public class GroupedLastByAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedByValueConsumer {
 
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedLastByAccumulator.class);
 
   private final TSDataType xDataType;
   private final TSDataType yDataType;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedValueSerializer valueSerializer;
+  private final TypeServices.GroupedByInputReader inputReader;
+  private final TypeServices.GroupedByValueDeserializer valueDeserializer;
 
   private final LongBigArray yLastTimes = new LongBigArray(Long.MIN_VALUE);
 
@@ -71,71 +77,18 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
   public GroupedLastByAccumulator(TSDataType xDataType, TSDataType yDataType) {
     this.xDataType = xDataType;
     this.yDataType = yDataType;
-
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues = new IntBigArray();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues = new LongBigArray();
-        break;
-      case FLOAT:
-        xFloatValues = new FloatBigArray();
-        break;
-      case DOUBLE:
-        xDoubleValues = new DoubleBigArray();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        xBinaryValues = new BinaryBigArray();
-        break;
-      case BOOLEAN:
-        xBooleanValues = new BooleanBigArray();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
+    Type type = Type.fromTsDataType(xDataType);
+    this.valueService = TypeServices.GROUPED_VALUE_SERVICE.call(type);
+    this.valueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_BY_INPUT_READER_SERVICE.call(type);
+    this.valueDeserializer = TypeServices.GROUPED_BY_VALUE_DESERIALIZER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += xIntValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += xLongValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += xFloatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += xDoubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += xBinaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += xBooleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
-
     return INSTANCE_SIZE
-        + valuesSize
+        + valueService.sizeOf(this)
         + yLastTimes.sizeOf()
         + inits.sizeOf()
         + initNullTimeValues.sizeOf()
@@ -148,34 +101,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
     inits.ensureCapacity(groupCount);
     initNullTimeValues.ensureCapacity(groupCount);
     xNulls.ensureCapacity(groupCount);
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        xFloatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        xDoubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        xBinaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        xBooleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
@@ -187,34 +113,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
     inits.reset();
     initNullTimeValues.reset();
     xNulls.reset();
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.reset();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.reset();
-        break;
-      case FLOAT:
-        xFloatValues.reset();
-        break;
-      case DOUBLE:
-        xDoubleValues.reset();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        xBinaryValues.reset();
-        break;
-      case BOOLEAN:
-        xBooleanValues.reset();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
+    valueService.reset(this);
   }
 
   @Override
@@ -222,34 +121,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
     checkArgument(arguments.length == 3, "Length of input Column[] for LAST_BY should be 3");
 
     // arguments[0] is x column, arguments[1] is y column, arguments[2] is time column
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], arguments[1], arguments[2], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
@@ -274,73 +146,8 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
       offset += 1;
       int groupId = groupIds[i];
 
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          int intVal = isXValueNull ? 0 : BytesUtils.bytesToInt(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateIntLastValue(groupId, isXValueNull, intVal, curTime);
-          } else {
-            updateIntNullTimeValue(groupId, isXValueNull, intVal);
-          }
-          break;
-        case INT64:
-        case TIMESTAMP:
-          long longVal =
-              isXValueNull ? 0 : BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-          if (!isOrderTimeNull) {
-            updateLongLastValue(groupId, isXValueNull, longVal, curTime);
-          } else {
-            updateLongNullTimeValue(groupId, isXValueNull, longVal);
-          }
-          break;
-        case FLOAT:
-          float floatVal = isXValueNull ? 0 : BytesUtils.bytesToFloat(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateFloatLastValue(groupId, isXValueNull, floatVal, curTime);
-          } else {
-            updateFloatNullTimeValue(groupId, isXValueNull, floatVal);
-          }
-          break;
-        case DOUBLE:
-          double doubleVal = isXValueNull ? 0 : BytesUtils.bytesToDouble(bytes, offset);
-          if (!isOrderTimeNull) {
-            updateDoubleLastValue(groupId, isXValueNull, doubleVal, curTime);
-          } else {
-            updateDoubleNullTimeValue(groupId, isXValueNull, doubleVal);
-          }
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          Binary binaryVal = null;
-          if (!isXValueNull) {
-            int length = BytesUtils.bytesToInt(bytes, offset);
-            offset += Integer.BYTES;
-            binaryVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-          }
-          if (!isOrderTimeNull) {
-            updateBinaryLastValue(groupId, isXValueNull, binaryVal, curTime);
-          } else {
-            updateBinaryNullTimeValue(groupId, isXValueNull, binaryVal);
-          }
-          break;
-        case BOOLEAN:
-          boolean boolVal = false;
-          if (!isXValueNull) {
-            boolVal = BytesUtils.bytesToBool(bytes, offset);
-          }
-          if (!isOrderTimeNull) {
-            updateBooleanLastValue(groupId, isXValueNull, boolVal, curTime);
-          } else {
-            updateBooleanNullTimeValue(groupId, isXValueNull, boolVal);
-          }
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-      }
+      valueDeserializer.deserialize(
+          bytes, offset, groupId, isXValueNull, curTime, isOrderTimeNull, this);
     }
   }
 
@@ -359,7 +166,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
 
   private byte[] serializeTimeWithValue(int groupId) {
     boolean xNull = xNulls.get(groupId);
-    int length = Long.BYTES + 2 + (xNull ? 0 : calculateValueLength(groupId));
+    int length = Long.BYTES + 2 + (xNull ? 0 : valueSerializer.calcTypeSize(this, groupId));
     byte[] bytes = new byte[length];
     boolean isOrderTimeNull = !inits.get(groupId);
 
@@ -369,65 +176,11 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
 
     if (!xNull) {
       int valueOffset = Long.BYTES + 2;
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          intToBytes(xIntValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case INT64:
-        case TIMESTAMP:
-          longToBytes(xLongValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case FLOAT:
-          floatToBytes(xFloatValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case DOUBLE:
-          doubleToBytes(xDoubleValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          byte[] values = xBinaryValues.get(groupId).getValues();
-          intToBytes(values.length, bytes, valueOffset);
-          System.arraycopy(values, 0, bytes, length - values.length, values.length);
-          return bytes;
-        case BOOLEAN:
-          boolToBytes(xBooleanValues.get(groupId), bytes, valueOffset);
-          return bytes;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-      }
+      valueSerializer.serialize(this, groupId, bytes, valueOffset);
     }
     return bytes;
   }
 
-  private int calculateValueLength(int groupId) {
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        return Integer.BYTES;
-      case INT64:
-      case TIMESTAMP:
-        return Long.BYTES;
-      case FLOAT:
-        return Float.BYTES;
-      case DOUBLE:
-        return Double.BYTES;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        return Integer.BYTES + xBinaryValues.get(groupId).getValues().length;
-      case BOOLEAN:
-        return 1;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
-  }
-
   @Override
   public void evaluateFinal(int groupId, ColumnBuilder columnBuilder) {
     if (xNulls.get(groupId)) {
@@ -435,34 +188,7 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
       return;
     }
 
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xIntValues.get(groupId));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xLongValues.get(groupId));
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xFloatValues.get(groupId));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xDoubleValues.get(groupId));
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        columnBuilder.writeBinary(xBinaryValues.get(groupId));
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xBooleanValues.get(groupId));
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
-    }
+    valueService.write(this, groupId, columnBuilder);
   }
 
   private boolean checkAndUpdateLastTime(int groupId, boolean isXValueNull, long curTime) {
@@ -727,4 +453,125 @@ public class GroupedLastByAccumulator implements GroupedAccumulator {
       xBooleanValues.set(groupId, xValue);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_LAST_BY_AGGREGATION, xDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    xIntValues = new IntBigArray();
+  }
+
+  @Override
+  public void initializeLongValues() {
+    xLongValues = new LongBigArray();
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    xFloatValues = new FloatBigArray();
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    xDoubleValues = new DoubleBigArray();
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    xBinaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    xBooleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return xIntValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return xLongValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return xFloatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return xDoubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return xBinaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return xBooleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, boolean xNull, int value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateIntNullTimeValue(groupId, xNull, value);
+    } else {
+      updateIntLastValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateLong(int groupId, boolean xNull, long value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateLongNullTimeValue(groupId, xNull, value);
+    } else {
+      updateLongLastValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateFloat(int groupId, boolean xNull, float value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateFloatNullTimeValue(groupId, xNull, value);
+    } else {
+      updateFloatLastValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateDouble(int groupId, boolean xNull, double value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateDoubleNullTimeValue(groupId, xNull, value);
+    } else {
+      updateDoubleLastValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateBinary(int groupId, boolean xNull, Binary value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBinaryNullTimeValue(groupId, xNull, value);
+    } else {
+      updateBinaryLastValue(groupId, xNull, value, time);
+    }
+  }
+
+  @Override
+  public void updateBoolean(
+      int groupId, boolean xNull, boolean value, boolean timeNull, long time) {
+    if (timeNull) {
+      updateBooleanNullTimeValue(groupId, xNull, value);
+    } else {
+      updateBooleanLastValue(groupId, xNull, value, time);
+    }
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
index baea2267935..2b685134730 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxAccumulator.java
@@ -27,15 +27,20 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
 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.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
-public class GroupedMaxAccumulator implements GroupedAccumulator {
+public class GroupedMaxAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedInputConsumer {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedMaxAccumulator.class);
   private final TSDataType seriesDataType;
@@ -48,178 +53,38 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
   private DoubleBigArray doubleValues;
   private BinaryBigArray binaryValues;
   private BooleanBigArray booleanValues;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedInputReader inputReader;
 
   public GroupedMaxAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        // Use MIN_VALUE to init NumericType to optimize updateValue method
-        intValues = new IntBigArray(Integer.MIN_VALUE);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues = new LongBigArray(Long.MIN_VALUE);
-        return;
-      case FLOAT:
-        floatValues = new FloatBigArray(Float.MIN_VALUE);
-        return;
-      case DOUBLE:
-        doubleValues = new DoubleBigArray(Double.MIN_VALUE);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues = new BinaryBigArray();
-        return;
-      case BOOLEAN:
-        booleanValues = new BooleanBigArray();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-    }
+    Type type = Type.fromTsDataType(seriesDataType);
+    this.valueService = TypeServices.GROUPED_VALUE_SERVICE.call(type);
+    this.inputReader = TypeServices.GROUPED_INPUT_READER_SERVICE.call(type);
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += intValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += longValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += floatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += doubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += binaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += booleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-    }
-
-    return INSTANCE_SIZE + valuesSize + inits.sizeOf();
+    return INSTANCE_SIZE + valueService.sizeOf(this) + inits.sizeOf();
   }
 
   @Override
   public void setGroupCount(long groupCount) {
     inits.ensureCapacity(groupCount);
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        floatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        doubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        booleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
   public void addIntermediate(int[] groupIds, Column argument) {
 
-    for (int i = 0; i < groupIds.length; i++) {
-      if (argument.isNull(i)) {
-        continue;
-      }
-
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          updateIntValue(groupIds[i], argument.getInt(i));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          updateLongValue(groupIds[i], argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatValue(groupIds[i], argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleValue(groupIds[i], argument.getDouble(i));
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          updateBinaryValue(groupIds[i], argument.getBinary(i));
-          break;
-        case BOOLEAN:
-          updateBooleanValue(groupIds[i], argument.getBoolean(i));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-      }
-    }
+    inputReader.addIntermediate(groupIds, argument, this);
   }
 
   @Override
@@ -228,34 +93,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          columnBuilder.writeBinary(binaryValues.get(groupId));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(booleanValues.get(groupId));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -264,34 +102,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          columnBuilder.writeBinary(binaryValues.get(groupId));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(booleanValues.get(groupId));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -301,34 +112,7 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
   @Override
   public void reset() {
     inits.reset();
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.reset();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.reset();
-        return;
-      case FLOAT:
-        floatValues.reset();
-        return;
-      case DOUBLE:
-        doubleValues.reset();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.reset();
-        return;
-      case BOOLEAN:
-        booleanValues.reset();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
-    }
+    valueService.reset(this);
   }
 
   private void addIntInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
@@ -503,4 +287,100 @@ public class GroupedMaxAccumulator implements GroupedAccumulator {
       booleanValues.set(groupId, value);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_AGGREGATION, seriesDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    intValues = new IntBigArray(Integer.MIN_VALUE);
+  }
+
+  @Override
+  public void initializeLongValues() {
+    longValues = new LongBigArray(Long.MIN_VALUE);
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    floatValues = new FloatBigArray(Float.MIN_VALUE);
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    doubleValues = new DoubleBigArray(Double.MIN_VALUE);
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    binaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    booleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return intValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return longValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return floatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return doubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return binaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return booleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, int value) {
+    updateIntValue(groupId, value);
+  }
+
+  @Override
+  public void updateLong(int groupId, long value) {
+    updateLongValue(groupId, value);
+  }
+
+  @Override
+  public void updateFloat(int groupId, float value) {
+    updateFloatValue(groupId, value);
+  }
+
+  @Override
+  public void updateDouble(int groupId, double value) {
+    updateDoubleValue(groupId, value);
+  }
+
+  @Override
+  public void updateBinary(int groupId, Binary value) {
+    updateBinaryValue(groupId, value);
+  }
+
+  @Override
+  public void updateBoolean(int groupId, boolean value) {
+    updateBooleanValue(groupId, value);
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
index a8007085669..5d97c9b01e7 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java
@@ -38,7 +38,6 @@ 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.BytesUtils;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
@@ -48,13 +47,24 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static org.apache.tsfile.utils.BytesUtils.boolToBytes;
 
 /** max(x,y) returns the value of x associated with the maximum value of y over all input values. */
-public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulator {
+public abstract class GroupedMaxMinByBaseAccumulator
+    implements GroupedAccumulator, TypeServices.GroupedMaxMinByValueUpdater {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedAccumulator.class);
 
   private final TSDataType xDataType;
 
   private final TSDataType yDataType;
+  private final Type xType;
+  private final TypeServices.GroupedValueService xValueService;
+  private final TypeServices.GroupedValueService yValueService;
+  private final TypeServices.GroupedValueSerializer xValueSerializer;
+  private final TypeServices.GroupedValueSerializer yValueSerializer;
+  private final TypeServices.GroupedValueSetter xValueSetter;
+  private final TypeServices.GroupedMaxMinByInput inputReader;
+  private final TypeServices.GroupedMaxMinByIntermediateInput intermediateInputReader;
+  private final TypeServices.GroupedValueAccessor xValueAccessor = new ValueAccessor(true);
+  private final TypeServices.GroupedValueAccessor yValueAccessor = new ValueAccessor(false);
 
   private final BooleanBigArray inits = new BooleanBigArray();
 
@@ -77,13 +87,18 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
   protected GroupedMaxMinByBaseAccumulator(TSDataType xDataType, TSDataType yDataType) {
     this.xDataType = xDataType;
     this.yDataType = yDataType;
-
-    TypeServices.INTERMEDIATE_VALUE_INITIALIZER_SERVICE
-        .call(Type.fromTsDataType(xDataType))
-        .initialize(this, true);
-    TypeServices.INTERMEDIATE_VALUE_INITIALIZER_SERVICE
-        .call(Type.fromTsDataType(yDataType))
-        .initialize(this, false);
+    this.xType = Type.fromTsDataType(xDataType);
+    Type yType = Type.fromTsDataType(yDataType);
+    this.xValueService = TypeServices.GROUPED_VALUE_SERVICE.call(xType);
+    this.yValueService = TypeServices.GROUPED_VALUE_SERVICE.call(yType);
+    this.xValueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(xType);
+    this.yValueSerializer = TypeServices.GROUPED_VALUE_SERIALIZER_SERVICE.call(yType);
+    this.xValueSetter = TypeServices.GROUPED_VALUE_SETTER_SERVICE.call(xType);
+    this.inputReader = TypeServices.GROUPED_MAX_MIN_BY_INPUT_SERVICE.call(yType);
+    this.intermediateInputReader =
+        TypeServices.GROUPED_MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE.call(yType);
+    xValueService.initialize(xValueAccessor);
+    yValueService.initialize(yValueAccessor);
   }
 
   public LongBigArray getXLongValues() {
@@ -184,132 +199,19 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += xIntValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += xLongValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += xFloatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += xDoubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += xBinaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += xBooleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
-
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += yIntValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += yLongValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += yFloatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += yDoubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += yBinaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += yBooleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
-
-    return INSTANCE_SIZE + valuesSize + inits.sizeOf() + xNulls.sizeOf();
+    return INSTANCE_SIZE
+        + xValueService.sizeOf(xValueAccessor)
+        + yValueService.sizeOf(yValueAccessor)
+        + inits.sizeOf()
+        + xNulls.sizeOf();
   }
 
   @Override
   public void setGroupCount(long groupCount) {
     inits.ensureCapacity(groupCount);
     xNulls.ensureCapacity(groupCount);
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.ensureCapacity(groupCount);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.ensureCapacity(groupCount);
-        break;
-      case FLOAT:
-        xFloatValues.ensureCapacity(groupCount);
-        break;
-      case DOUBLE:
-        xDoubleValues.ensureCapacity(groupCount);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        xBinaryValues.ensureCapacity(groupCount);
-        break;
-      case BOOLEAN:
-        xBooleanValues.ensureCapacity(groupCount);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        yIntValues.ensureCapacity(groupCount);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        yLongValues.ensureCapacity(groupCount);
-        break;
-      case FLOAT:
-        yFloatValues.ensureCapacity(groupCount);
-        break;
-      case DOUBLE:
-        yDoubleValues.ensureCapacity(groupCount);
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        yBinaryValues.ensureCapacity(groupCount);
-        break;
-      case BOOLEAN:
-        yBooleanValues.ensureCapacity(groupCount);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
+    xValueService.ensureCapacity(xValueAccessor, groupCount);
+    yValueService.ensureCapacity(yValueAccessor, groupCount);
   }
 
   @Override
@@ -319,98 +221,13 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
   public void reset() {
     inits.reset();
     xNulls.reset();
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        xIntValues.reset();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        xLongValues.reset();
-        break;
-      case FLOAT:
-        xFloatValues.reset();
-        break;
-      case DOUBLE:
-        xDoubleValues.reset();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        xBinaryValues.reset();
-        break;
-      case BOOLEAN:
-        xBooleanValues.reset();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
-
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        yIntValues.reset();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        yLongValues.reset();
-        break;
-      case FLOAT:
-        yFloatValues.reset();
-        break;
-      case DOUBLE:
-        yDoubleValues.reset();
-        break;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        yBinaryValues.reset();
-        break;
-      case BOOLEAN:
-        yBooleanValues.reset();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
-    }
+    xValueService.reset(xValueAccessor);
+    yValueService.reset(yValueAccessor);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments, mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments, mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments, mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments, mask);
-        return;
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-        addBinaryInput(groupIds, arguments, mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments, mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
-    }
+    inputReader.add(groupIds, arguments, mask, this);
   }
 
   @Override
@@ -453,29 +270,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     writeX(groupId, columnBuilder);
   }
 
-  private void addIntInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateIntResult(groupIds[i], arguments[1].getInt(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateIntResult(
-              groupIds[position], arguments[1].getInt(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateIntResult(int groupId, int yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateInt(int groupId, int yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yIntValues.get(groupId))) {
       inits.set(groupId, true);
       yIntValues.set(groupId, yValue);
@@ -483,29 +279,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     }
   }
 
-  private void addLongInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateLongResult(groupIds[i], arguments[1].getLong(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateLongResult(
-              groupIds[position], arguments[1].getLong(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateLongResult(int groupId, long yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateLong(int groupId, long yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yLongValues.get(groupId))) {
       inits.set(groupId, true);
       yLongValues.set(groupId, yValue);
@@ -513,29 +288,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     }
   }
 
-  private void addFloatInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateFloatResult(groupIds[i], arguments[1].getFloat(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateFloatResult(
-              groupIds[position], arguments[1].getFloat(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateFloatResult(int groupId, float yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateFloat(int groupId, float yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yFloatValues.get(groupId))) {
       inits.set(groupId, true);
       yFloatValues.set(groupId, yValue);
@@ -543,29 +297,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     }
   }
 
-  private void addDoubleInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateDoubleResult(groupIds[i], arguments[1].getDouble(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateDoubleResult(
-              groupIds[position], arguments[1].getDouble(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateDoubleResult(int groupId, double yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateDouble(int groupId, double yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yDoubleValues.get(groupId))) {
       inits.set(groupId, true);
       yDoubleValues.set(groupId, yValue);
@@ -573,29 +306,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     }
   }
 
-  private void addBinaryInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateBinaryResult(groupIds[i], arguments[1].getBinary(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateBinaryResult(
-              groupIds[position], arguments[1].getBinary(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateBinaryResult(int groupId, Binary yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateBinary(int groupId, Binary yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yBinaryValues.get(groupId))) {
       inits.set(groupId, true);
       yBinaryValues.set(groupId, yValue);
@@ -603,29 +315,8 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
     }
   }
 
-  private void addBooleanInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!arguments[1].isNull(i)) {
-          updateBooleanResult(groupIds[i], arguments[1].getBoolean(i), arguments[0], i);
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!arguments[1].isNull(position)) {
-          updateBooleanResult(
-              groupIds[position], arguments[1].getBoolean(position), arguments[0], position);
-        }
-      }
-    }
-  }
-
-  private void updateBooleanResult(int groupId, boolean yValue, Column xColumn, int xIndex) {
+  @Override
+  public void updateBoolean(int groupId, boolean yValue, Column xColumn, int xIndex) {
     if (!inits.get(groupId) || check(yValue, yBooleanValues.get(groupId))) {
       inits.set(groupId, true);
       yBooleanValues.set(groupId, yValue);
@@ -638,35 +329,7 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
       columnBuilder.appendNull();
       return;
     }
-    switch (xDataType) {
-      case INT32:
-      case DATE:
-        columnBuilder.writeInt(xIntValues.get(groupId));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        columnBuilder.writeLong(xLongValues.get(groupId));
-        break;
-      case FLOAT:
-        columnBuilder.writeFloat(xFloatValues.get(groupId));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(xDoubleValues.get(groupId));
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        columnBuilder.writeBinary(xBinaryValues.get(groupId));
-        break;
-      case BOOLEAN:
-        columnBuilder.writeBoolean(xBooleanValues.get(groupId));
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-    }
+    xValueService.write(xValueAccessor, groupId, columnBuilder);
   }
 
   private void updateX(int groupId, Column xColumn, int xIndex) {
@@ -674,163 +337,128 @@ public abstract class GroupedMaxMinByBaseAccumulator implements GroupedAccumulat
       xNulls.set(groupId, true);
     } else {
       xNulls.set(groupId, false);
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          xIntValues.set(groupId, xColumn.getInt(xIndex));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          xLongValues.set(groupId, xColumn.getLong(xIndex));
-          break;
-        case FLOAT:
-          xFloatValues.set(groupId, xColumn.getFloat(xIndex));
-          break;
-        case DOUBLE:
-          xDoubleValues.set(groupId, xColumn.getDouble(xIndex));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          xBinaryValues.set(groupId, xColumn.getBinary(xIndex));
-          break;
-        case BOOLEAN:
-          xBooleanValues.set(groupId, xColumn.getBoolean(xIndex));
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
-      }
+      xValueSetter.set(xValueAccessor, groupId, xColumn, xIndex);
     }
   }
 
   private byte[] serialize(int groupId) {
     boolean xNull = xNulls.get(groupId);
-    int yLength = calculateValueLength(groupId, yDataType, false);
-    int length = yLength + 1 + (xNull ? 0 : calculateValueLength(groupId, xDataType, true));
+    int yLength = yValueSerializer.calcTypeSize(yValueAccessor, groupId);
+    int length = yLength + 1 + (xNull ? 0 : xValueSerializer.calcTypeSize(xValueAccessor, groupId));
     byte[] bytes = new byte[length];
 
-    writeIntermediate(groupId, false, yDataType, bytes, 0);
+    yValueSerializer.serialize(yValueAccessor, groupId, bytes, 0);
     boolToBytes(xNull, bytes, yLength);
     if (!xNull) {
-      writeIntermediate(groupId, true, xDataType, bytes, yLength + 1);
+      xValueSerializer.serialize(xValueAccessor, groupId, bytes, yLength + 1);
     }
 
     return bytes;
   }
 
-  private void writeIntermediate(
-      int groupId, boolean isX, TSDataType dataType, byte[] bytes, int offset) {
-    TypeServices.INTERMEDIATE_VALUE_WRITER_SERVICE
-        .call(Type.fromTsDataType(dataType))
-        .write(this, isX, groupId, bytes, offset);
-  }
-
-  private int calculateValueLength(int groupId, TSDataType dataType, boolean isX) {
-    Object value =
-        dataType.isBinary()
-            ? (isX ? xBinaryValues.get(groupId) : yBinaryValues.get(groupId))
-            : null;
-    return Type.fromTsDataType(dataType).calcTypeSize(value);
-  }
-
   private void updateFromBytesIntermediateInput(int groupId, byte[] bytes) {
-    // long time = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 0);
-    int offset = 0;
-    // Use Column to store x value
     TsBlockBuilder builder = new TsBlockBuilder(Collections.singletonList(xDataType));
     ColumnBuilder columnBuilder = builder.getValueColumnBuilders()[0];
-    switch (yDataType) {
-      case INT32:
-      case DATE:
-        int intMaxVal = BytesUtils.bytesToInt(bytes, offset);
-        offset += Integer.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateIntResult(groupId, intMaxVal, columnBuilder.build(), 0);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        long longMaxVal = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset);
-        offset += Long.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateLongResult(groupId, longMaxVal, columnBuilder.build(), 0);
-        break;
-      case FLOAT:
-        float floatMaxVal = BytesUtils.bytesToFloat(bytes, offset);
-        offset += Float.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateFloatResult(groupId, floatMaxVal, columnBuilder.build(), 0);
-        break;
-      case DOUBLE:
-        double doubleMaxVal = BytesUtils.bytesToDouble(bytes, offset);
-        offset += Long.BYTES;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateDoubleResult(groupId, doubleMaxVal, columnBuilder.build(), 0);
-        break;
-      case STRING:
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-        int length = BytesUtils.bytesToInt(bytes, offset);
-        offset += Integer.BYTES;
-        Binary binaryMaxVal = new Binary(BytesUtils.subBytes(bytes, offset, length));
-        offset += length;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateBinaryResult(groupId, binaryMaxVal, columnBuilder.build(), 0);
-        break;
-      case BOOLEAN:
-        boolean booleanVal = BytesUtils.bytesToBool(bytes, offset);
-        offset += 1;
-        readXFromBytesIntermediateInput(bytes, offset, columnBuilder);
-        updateBooleanResult(groupId, booleanVal, columnBuilder.build(), 0);
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(
-                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, yDataType));
-    }
+    intermediateInputReader.update(groupId, bytes, xType, columnBuilder, this);
   }
 
-  private void readXFromBytesIntermediateInput(
-      byte[] bytes, int offset, ColumnBuilder columnBuilder) {
-    boolean isXNull = BytesUtils.bytesToBool(bytes, offset);
-    offset += 1;
-    if (isXNull) {
-      columnBuilder.appendNull();
-    } else {
-      switch (xDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(BytesUtils.bytesToInt(bytes, offset));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(BytesUtils.bytesToLongFromOffset(bytes, 8, offset));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(BytesUtils.bytesToFloat(bytes, offset));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(BytesUtils.bytesToDouble(bytes, offset));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          int length = BytesUtils.bytesToInt(bytes, offset);
-          offset += Integer.BYTES;
-          columnBuilder.writeBinary(new Binary(BytesUtils.subBytes(bytes, offset, length)));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(BytesUtils.bytesToBool(bytes, offset));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(
-                  CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, xDataType));
+  private final class ValueAccessor implements TypeServices.GroupedValueAccessor {
+    private final boolean x;
+
+    private ValueAccessor(boolean x) {
+      this.x = x;
+    }
+
+    @Override
+    public RuntimeException unsupportedException() {
+      TSDataType dataType = x ? xDataType : yDataType;
+      return new UnSupportedDataTypeException(
+          String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MAX_BY_MIN_BY_AGGREGATION, dataType));
+    }
+
+    @Override
+    public void initializeIntValues() {
+      if (x) {
+        xIntValues = new IntBigArray();
+      } else {
+        yIntValues = new IntBigArray();
+      }
+    }
+
+    @Override
+    public void initializeLongValues() {
+      if (x) {
+        xLongValues = new LongBigArray();
+      } else {
+        yLongValues = new LongBigArray();
+      }
+    }
+
+    @Override
+    public void initializeFloatValues() {
+      if (x) {
+        xFloatValues = new FloatBigArray();
+      } else {
+        yFloatValues = new FloatBigArray();
+      }
+    }
+
+    @Override
+    public void initializeDoubleValues() {
+      if (x) {
+        xDoubleValues = new DoubleBigArray();
+      } else {
+        yDoubleValues = new DoubleBigArray();
+      }
+    }
+
+    @Override
+    public void initializeBinaryValues() {
+      if (x) {
+        xBinaryValues = new BinaryBigArray();
+      } else {
+        yBinaryValues = new BinaryBigArray();
+      }
+    }
+
+    @Override
+    public void initializeBooleanValues() {
+      if (x) {
+        xBooleanValues = new BooleanBigArray();
+      } else {
+        yBooleanValues = new BooleanBigArray();
       }
     }
+
+    @Override
+    public IntBigArray getIntValues() {
+      return x ? xIntValues : yIntValues;
+    }
+
+    @Override
+    public LongBigArray getLongValues() {
+      return x ? xLongValues : yLongValues;
+    }
+
+    @Override
+    public FloatBigArray getFloatValues() {
+      return x ? xFloatValues : yFloatValues;
+    }
+
+    @Override
+    public DoubleBigArray getDoubleValues() {
+      return x ? xDoubleValues : yDoubleValues;
+    }
+
+    @Override
+    public BinaryBigArray getBinaryValues() {
+      return x ? xBinaryValues : yBinaryValues;
+    }
+
+    @Override
+    public BooleanBigArray getBooleanValues() {
+      return x ? xBooleanValues : yBooleanValues;
+    }
   }
 
   /**
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
index 9eb335c6f47..ab708678d53 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMinAccumulator.java
@@ -27,15 +27,20 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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;
 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.RamUsageEstimator;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
-public class GroupedMinAccumulator implements GroupedAccumulator {
+public class GroupedMinAccumulator
+    implements GroupedAccumulator,
+        TypeServices.GroupedValueAccessor,
+        TypeServices.GroupedInputConsumer {
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(GroupedMinAccumulator.class);
   private final TSDataType seriesDataType;
@@ -48,178 +53,38 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
   private DoubleBigArray doubleValues;
   private BinaryBigArray binaryValues;
   private BooleanBigArray booleanValues;
+  private final TypeServices.GroupedValueService valueService;
+  private final TypeServices.GroupedInputReader inputReader;
 
   public GroupedMinAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        // Use MAX_VALUE to init NumericType to optimize updateValue method
-        intValues = new IntBigArray(Integer.MAX_VALUE);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues = new LongBigArray(Long.MAX_VALUE);
-        return;
-      case FLOAT:
-        floatValues = new FloatBigArray(Float.MAX_VALUE);
-        return;
-      case DOUBLE:
-        doubleValues = new DoubleBigArray(Double.MAX_VALUE);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues = new BinaryBigArray();
-        return;
-      case BOOLEAN:
-        booleanValues = new BooleanBigArray();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-    }
+    this.valueService =
+        TypeServices.GROUPED_VALUE_SERVICE.call(Type.fromTsDataType(seriesDataType));
+    this.inputReader =
+        TypeServices.GROUPED_INPUT_READER_SERVICE.call(Type.fromTsDataType(seriesDataType));
+    valueService.initialize(this);
   }
 
   @Override
   public long getEstimatedSize() {
-    long valuesSize = 0;
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        valuesSize += intValues.sizeOf();
-        break;
-      case INT64:
-      case TIMESTAMP:
-        valuesSize += longValues.sizeOf();
-        break;
-      case FLOAT:
-        valuesSize += floatValues.sizeOf();
-        break;
-      case DOUBLE:
-        valuesSize += doubleValues.sizeOf();
-        break;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        valuesSize += binaryValues.sizeOf();
-        break;
-      case BOOLEAN:
-        valuesSize += booleanValues.sizeOf();
-        break;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-    }
-
-    return INSTANCE_SIZE + valuesSize + inits.sizeOf();
+    return INSTANCE_SIZE + valueService.sizeOf(this) + inits.sizeOf();
   }
 
   @Override
   public void setGroupCount(long groupCount) {
     inits.ensureCapacity(groupCount);
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.ensureCapacity(groupCount);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.ensureCapacity(groupCount);
-        return;
-      case FLOAT:
-        floatValues.ensureCapacity(groupCount);
-        return;
-      case DOUBLE:
-        doubleValues.ensureCapacity(groupCount);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.ensureCapacity(groupCount);
-        return;
-      case BOOLEAN:
-        booleanValues.ensureCapacity(groupCount);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-    }
+    valueService.ensureCapacity(this, groupCount);
   }
 
   @Override
   public void addInput(int[] groupIds, Column[] arguments, AggregationMask mask) {
 
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        addIntInput(groupIds, arguments[0], mask);
-        return;
-      case INT64:
-      case TIMESTAMP:
-        addLongInput(groupIds, arguments[0], mask);
-        return;
-      case FLOAT:
-        addFloatInput(groupIds, arguments[0], mask);
-        return;
-      case DOUBLE:
-        addDoubleInput(groupIds, arguments[0], mask);
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        addBinaryInput(groupIds, arguments[0], mask);
-        return;
-      case BOOLEAN:
-        addBooleanInput(groupIds, arguments[0], mask);
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-    }
+    inputReader.addInput(groupIds, arguments, mask, this);
   }
 
   @Override
   public void addIntermediate(int[] groupIds, Column argument) {
-
-    for (int i = 0; i < groupIds.length; i++) {
-      if (argument.isNull(i)) {
-        continue;
-      }
-
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          updateIntValue(groupIds[i], argument.getInt(i));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          updateLongValue(groupIds[i], argument.getLong(i));
-          break;
-        case FLOAT:
-          updateFloatValue(groupIds[i], argument.getFloat(i));
-          break;
-        case DOUBLE:
-          updateDoubleValue(groupIds[i], argument.getDouble(i));
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          updateBinaryValue(groupIds[i], argument.getBinary(i));
-          break;
-        case BOOLEAN:
-          updateBooleanValue(groupIds[i], argument.getBoolean(i));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-      }
-    }
+    inputReader.addIntermediate(groupIds, argument, this);
   }
 
   @Override
@@ -228,34 +93,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case STRING:
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-          columnBuilder.writeBinary(binaryValues.get(groupId));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(booleanValues.get(groupId));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -264,34 +102,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     if (!inits.get(groupId)) {
       columnBuilder.appendNull();
     } else {
-      switch (seriesDataType) {
-        case INT32:
-        case DATE:
-          columnBuilder.writeInt(intValues.get(groupId));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          columnBuilder.writeLong(longValues.get(groupId));
-          break;
-        case FLOAT:
-          columnBuilder.writeFloat(floatValues.get(groupId));
-          break;
-        case DOUBLE:
-          columnBuilder.writeDouble(doubleValues.get(groupId));
-          break;
-        case TEXT:
-        case BLOB:
-        case OBJECT:
-        case STRING:
-          columnBuilder.writeBinary(binaryValues.get(groupId));
-          break;
-        case BOOLEAN:
-          columnBuilder.writeBoolean(booleanValues.get(groupId));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-      }
+      valueService.write(this, groupId, columnBuilder);
     }
   }
 
@@ -301,55 +112,7 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
   @Override
   public void reset() {
     inits.reset();
-    switch (seriesDataType) {
-      case INT32:
-      case DATE:
-        intValues.reset();
-        return;
-      case INT64:
-      case TIMESTAMP:
-        longValues.reset();
-        return;
-      case FLOAT:
-        floatValues.reset();
-        return;
-      case DOUBLE:
-        doubleValues.reset();
-        return;
-      case TEXT:
-      case STRING:
-      case BLOB:
-      case OBJECT:
-        binaryValues.reset();
-        return;
-      case BOOLEAN:
-        booleanValues.reset();
-        return;
-      default:
-        throw new UnSupportedDataTypeException(
-            String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
-    }
-  }
-
-  private void addIntInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateIntValue(groupIds[i], valueColumn.getInt(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateIntValue(groupIds[position], valueColumn.getInt(position));
-        }
-      }
-    }
+    valueService.reset(this);
   }
 
   protected void updateIntValue(int groupId, int value) {
@@ -360,27 +123,6 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addLongInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateLongValue(groupIds[i], valueColumn.getLong(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateLongValue(groupIds[position], valueColumn.getLong(position));
-        }
-      }
-    }
-  }
-
   protected void updateLongValue(int groupId, long value) {
     long min = longValues.get(groupId);
     if (value <= min) {
@@ -389,27 +131,6 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addFloatInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateFloatValue(groupIds[i], valueColumn.getFloat(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateFloatValue(groupIds[position], valueColumn.getFloat(position));
-        }
-      }
-    }
-  }
-
   protected void updateFloatValue(int groupId, float value) {
     float min = floatValues.get(groupId);
     if (value <= min) {
@@ -418,27 +139,6 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addDoubleInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateDoubleValue(groupIds[i], valueColumn.getDouble(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateDoubleValue(groupIds[position], valueColumn.getDouble(position));
-        }
-      }
-    }
-  }
-
   protected void updateDoubleValue(int groupId, double value) {
     double min = doubleValues.get(groupId);
     if (value <= min) {
@@ -447,27 +147,6 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addBinaryInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateBinaryValue(groupIds[i], valueColumn.getBinary(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateBinaryValue(groupIds[position], valueColumn.getBinary(position));
-        }
-      }
-    }
-  }
-
   protected void updateBinaryValue(int groupId, Binary value) {
     Binary min = binaryValues.get(groupId);
     if (!inits.get(groupId) || value.compareTo(min) < 0) {
@@ -476,31 +155,106 @@ public class GroupedMinAccumulator implements GroupedAccumulator {
     }
   }
 
-  private void addBooleanInput(int[] groupIds, Column valueColumn, AggregationMask mask) {
-    int positionCount = mask.getSelectedPositionCount();
-
-    if (mask.isSelectAll()) {
-      for (int i = 0; i < positionCount; i++) {
-        if (!valueColumn.isNull(i)) {
-          updateBooleanValue(groupIds[i], valueColumn.getBoolean(i));
-        }
-      }
-    } else {
-      int[] selectedPositions = mask.getSelectedPositions();
-      int position;
-      for (int i = 0; i < positionCount; i++) {
-        position = selectedPositions[i];
-        if (!valueColumn.isNull(position)) {
-          updateBooleanValue(groupIds[position], valueColumn.getBoolean(position));
-        }
-      }
-    }
-  }
-
   protected void updateBooleanValue(int groupId, boolean value) {
     if (!inits.get(groupId) || !value) {
       inits.set(groupId, true);
       booleanValues.set(groupId, value);
     }
   }
+
+  @Override
+  public RuntimeException unsupportedException() {
+    return new UnSupportedDataTypeException(
+        String.format(CalcMessages.UNSUPPORTED_DATA_TYPE_IN_MIN_AGGREGATION, seriesDataType));
+  }
+
+  @Override
+  public void initializeIntValues() {
+    intValues = new IntBigArray(Integer.MAX_VALUE);
+  }
+
+  @Override
+  public void initializeLongValues() {
+    longValues = new LongBigArray(Long.MAX_VALUE);
+  }
+
+  @Override
+  public void initializeFloatValues() {
+    floatValues = new FloatBigArray(Float.MAX_VALUE);
+  }
+
+  @Override
+  public void initializeDoubleValues() {
+    doubleValues = new DoubleBigArray(Double.MAX_VALUE);
+  }
+
+  @Override
+  public void initializeBinaryValues() {
+    binaryValues = new BinaryBigArray();
+  }
+
+  @Override
+  public void initializeBooleanValues() {
+    booleanValues = new BooleanBigArray();
+  }
+
+  @Override
+  public IntBigArray getIntValues() {
+    return intValues;
+  }
+
+  @Override
+  public LongBigArray getLongValues() {
+    return longValues;
+  }
+
+  @Override
+  public FloatBigArray getFloatValues() {
+    return floatValues;
+  }
+
+  @Override
+  public DoubleBigArray getDoubleValues() {
+    return doubleValues;
+  }
+
+  @Override
+  public BinaryBigArray getBinaryValues() {
+    return binaryValues;
+  }
+
+  @Override
+  public BooleanBigArray getBooleanValues() {
+    return booleanValues;
+  }
+
+  @Override
+  public void updateInt(int groupId, int value) {
+    updateIntValue(groupId, value);
+  }
+
+  @Override
+  public void updateLong(int groupId, long value) {
+    updateLongValue(groupId, value);
+  }
+
+  @Override
+  public void updateFloat(int groupId, float value) {
+    updateFloatValue(groupId, value);
+  }
+
+  @Override
+  public void updateDouble(int groupId, double value) {
+    updateDoubleValue(groupId, value);
+  }
+
+  @Override
+  public void updateBinary(int groupId, Binary value) {
+    updateBinaryValue(groupId, value);
+  }
+
+  @Override
+  public void updateBoolean(int groupId, boolean value) {
+    updateBooleanValue(groupId, value);
+  }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java
index 7d3c1ab9bc5..0073b968336 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java
@@ -43,12 +43,6 @@ import org.apache.iotdb.calc.transformation.dag.column.leaf.NullColumnTransforme
 import org.apache.iotdb.calc.transformation.dag.column.leaf.TimeColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.AbstractGreatestLeastColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.CoalesceColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InBinaryMultiColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InBooleanMultiColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InDoubleMultiColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InFloatMultiColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InInt32MultiColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.column.multi.InInt64MultiColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.InMultiColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.LogicalAndMultiColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.LogicalOrMultiColumnTransformer;
@@ -146,6 +140,7 @@ import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.TryCastFunct
 import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.UpperColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory;
 import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.NumericCodecStrategiesFactory;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.exception.SemanticException;
 import org.apache.iotdb.commons.queryengine.common.SessionInfo;
@@ -215,7 +210,6 @@ import org.apache.tsfile.read.common.block.column.LongColumn;
 import org.apache.tsfile.read.common.type.DateType;
 import org.apache.tsfile.read.common.type.TimestampType;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 import org.apache.tsfile.utils.Binary;
 
 import javax.annotation.Nullable;
@@ -224,11 +218,9 @@ import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -1509,7 +1501,7 @@ public class ColumnTransformerBuilder
         appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context);
       } else {
         ColumnTransformer childColumnTransformer = process(node.getValue(), context);
-        TypeEnum childTypeEnum = childColumnTransformer.getType().getTypeEnum();
+        Type childType = childColumnTransformer.getType();
         InListExpression inListExpression = (InListExpression) node.getValueList();
         List<Expression> expressionList = inListExpression.getValues();
         List<Literal> values = new ArrayList<>();
@@ -1523,7 +1515,7 @@ public class ColumnTransformerBuilder
           }
         }
         context.cache.put(
-            node, constructInColumnTransformer(childTypeEnum, valueColumnTransformerList, values));
+            node, constructInColumnTransformer(childType, valueColumnTransformerList, values));
       }
     }
 
@@ -1531,110 +1523,10 @@ public class ColumnTransformerBuilder
   }
 
   private static InMultiColumnTransformer constructInColumnTransformer(
-      TypeEnum childType,
-      List<ColumnTransformer> valueColumnTransformerList,
-      List<Literal> values) {
-    String errorMsg = "\"%s\" cannot be cast to [%s]";
-    switch (childType) {
-      case INT32:
-        Set<Integer> intSet = new HashSet<>();
-        for (Literal value : values) {
-          try {
-            long v = ((LongLiteral) value).getParsedValue();
-            if (v <= Integer.MAX_VALUE && v >= Integer.MIN_VALUE) {
-              intSet.add((int) v);
-            }
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        return new InInt32MultiColumnTransformer(intSet, valueColumnTransformerList);
-      case DATE:
-        Set<Integer> dateSet = new HashSet<>();
-        for (Literal value : values) {
-          dateSet.add(Integer.parseInt(((GenericLiteral) value).getValue()));
-        }
-        return new InInt32MultiColumnTransformer(dateSet, valueColumnTransformerList);
-      case INT64:
-        Set<Long> longSet = new HashSet<>();
-        for (Literal value : values) {
-          longSet.add(((LongLiteral) value).getParsedValue());
-        }
-        return new InInt64MultiColumnTransformer(longSet, valueColumnTransformerList);
-      case TIMESTAMP:
-        Set<Long> timestampSet = new HashSet<>();
-        for (Literal value : values) {
-          try {
-            if (value instanceof LongLiteral) {
-              timestampSet.add(((LongLiteral) value).getParsedValue());
-            } else if (value instanceof DoubleLiteral) {
-              timestampSet.add((long) ((DoubleLiteral) value).getValue());
-            } else if (value instanceof FloatLiteral) {
-              timestampSet.add((long) ((FloatLiteral) value).getValue());
-            } else if (value instanceof GenericLiteral) {
-              timestampSet.add(Long.parseLong(((GenericLiteral) value).getValue()));
-            } else {
-              throw new SemanticException(
-                  "InList Literal for TIMESTAMP can only be LongLiteral, DoubleLiteral and GenericLiteral, current is "
-                      + value.getClass().getSimpleName());
-            }
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        return new InInt64MultiColumnTransformer(timestampSet, valueColumnTransformerList);
-      case FLOAT:
-        Set<Float> floatSet = new HashSet<>();
-        for (Literal value : values) {
-          try {
-            if (value instanceof FloatLiteral) {
-              floatSet.add(((FloatLiteral) value).getValue());
-            } else {
-              floatSet.add((float) ((DoubleLiteral) value).getValue());
-            }
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        return new InFloatMultiColumnTransformer(floatSet, valueColumnTransformerList);
-      case DOUBLE:
-        Set<Double> doubleSet = new HashSet<>();
-        for (Literal value : values) {
-          try {
-            if (value instanceof FloatLiteral) {
-              doubleSet.add((double) ((FloatLiteral) value).getValue());
-            } else {
-              doubleSet.add(((DoubleLiteral) value).getValue());
-            }
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        return new InDoubleMultiColumnTransformer(doubleSet, valueColumnTransformerList);
-      case BOOLEAN:
-        Set<Boolean> booleanSet = new HashSet<>();
-        for (Literal value : values) {
-          booleanSet.add(((BooleanLiteral) value).getValue());
-        }
-        return new InBooleanMultiColumnTransformer(booleanSet, valueColumnTransformerList);
-      case TEXT:
-      case STRING:
-        Set<Binary> stringSet = new HashSet<>();
-        for (Literal value : values) {
-          stringSet.add(
-              new Binary(((StringLiteral) value).getValue(), TSFileConfig.STRING_CHARSET));
-        }
-        return new InBinaryMultiColumnTransformer(stringSet, valueColumnTransformerList);
-      case BLOB:
-        Set<Binary> binarySet = new HashSet<>();
-        for (Literal value : values) {
-          binarySet.add(new Binary(((BinaryLiteral) value).getValue()));
-        }
-        return new InBinaryMultiColumnTransformer(binarySet, valueColumnTransformerList);
-      default:
-        throw new UnsupportedOperationException(
-            CalcMessages.UNSUPPORTED_DATA_TYPE_LOWER + childType);
-    }
+      Type childType, List<ColumnTransformer> valueColumnTransformerList, List<Literal> values) {
+    return TypeServices.IN_MULTI_COLUMN_TRANSFORMER_SERVICE
+        .call(childType)
+        .create(valueColumnTransformerList, values);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
index 8b66d4eef27..f6afcab0cd2 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
@@ -42,12 +42,6 @@ import org.apache.iotdb.calc.execution.operator.process.TableTopKOperator;
 import org.apache.iotdb.calc.execution.operator.process.ValuesOperator;
 import org.apache.iotdb.calc.execution.operator.process.fill.IFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.ILinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.BinaryConstantFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.BooleanConstantFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.DoubleConstantFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.FloatConstantFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.IntConstantFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.constant.LongConstantFill;
 import org.apache.iotdb.calc.execution.operator.process.function.TableFunctionLeafOperator;
 import org.apache.iotdb.calc.execution.operator.process.function.TableFunctionOperator;
 import org.apache.iotdb.calc.execution.operator.process.gapfill.GapFillWGroupWMoOperator;
@@ -97,15 +91,6 @@ import org.apache.iotdb.calc.execution.relational.ColumnTransformerBuilder;
 import org.apache.iotdb.calc.i18n.CalcMessages;
 import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager;
 import org.apache.iotdb.calc.plan.relational.metadata.ITypeMetadata;
-import org.apache.iotdb.calc.plan.relational.planner.CastToBlobLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToBooleanLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToDateLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToDoubleLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToFloatLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToInt32LiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToInt64LiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToStringLiteralVisitor;
-import org.apache.iotdb.calc.plan.relational.planner.CastToTimestampLiteralVisitor;
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.leaf.LeafColumnTransformer;
 import org.apache.iotdb.calc.utils.TypeServices;
@@ -180,13 +165,11 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ListMultimap;
 import org.apache.tsfile.block.column.Column;
-import org.apache.tsfile.common.conf.TSFileConfig;
 import org.apache.tsfile.common.conf.TSFileDescriptor;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.block.TsBlock;
 import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.utils.Binary;
 
 import java.util.AbstractMap;
 import java.util.ArrayList;
@@ -214,9 +197,7 @@ import static org.apache.iotdb.calc.execution.operator.process.rowpattern.Physic
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AccumulatorFactory.createAccumulator;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AccumulatorFactory.createBuiltinAccumulator;
 import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AccumulatorFactory.createGroupedAccumulator;
-import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.IDENTITY_FILL;
 import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.TIME_COLUMN_TEMPLATE;
-import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.UNKNOWN_DATATYPE;
 import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.getLinearFill;
 import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.getNextFill;
 import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.getPreviousFill;
@@ -697,85 +678,10 @@ public abstract class TableOperatorGenerator<
       int inputColumnCount, List<TSDataType> inputDataTypes, Literal filledValue, C context) {
     IFill[] constantFill = new IFill[inputColumnCount];
     for (int i = 0; i < inputColumnCount; i++) {
-      switch (inputDataTypes.get(i)) {
-        case BOOLEAN:
-          Boolean bool = filledValue.accept(new CastToBooleanLiteralVisitor(), null);
-          if (bool == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new BooleanConstantFill(bool);
-          }
-          break;
-        case TEXT:
-        case STRING:
-          Binary binary =
-              filledValue.accept(new CastToStringLiteralVisitor(TSFileConfig.STRING_CHARSET), null);
-          if (binary == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new BinaryConstantFill(binary);
-          }
-          break;
-        case BLOB:
-          Binary blob = filledValue.accept(new CastToBlobLiteralVisitor(), null);
-          if (blob == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new BinaryConstantFill(blob);
-          }
-          break;
-        case INT32:
-          Integer intValue = filledValue.accept(new CastToInt32LiteralVisitor(), null);
-          if (intValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new IntConstantFill(intValue);
-          }
-          break;
-        case DATE:
-          Integer dateValue = filledValue.accept(new CastToDateLiteralVisitor(), null);
-          if (dateValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new IntConstantFill(dateValue);
-          }
-          break;
-        case INT64:
-          Long longValue = filledValue.accept(new CastToInt64LiteralVisitor(), null);
-          if (longValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new LongConstantFill(longValue);
-          }
-          break;
-        case TIMESTAMP:
-          Long timestampValue =
-              filledValue.accept(new CastToTimestampLiteralVisitor(context.getZoneId()), null);
-          if (timestampValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new LongConstantFill(timestampValue);
-          }
-          break;
-        case FLOAT:
-          Float floatValue = filledValue.accept(new CastToFloatLiteralVisitor(), null);
-          if (floatValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new FloatConstantFill(floatValue);
-          }
-          break;
-        case DOUBLE:
-          Double doubleValue = filledValue.accept(new CastToDoubleLiteralVisitor(), null);
-          if (doubleValue == null) {
-            constantFill[i] = IDENTITY_FILL;
-          } else {
-            constantFill[i] = new DoubleConstantFill(doubleValue);
-          }
-          break;
-        default:
-          throw new IllegalArgumentException(UNKNOWN_DATATYPE + inputDataTypes.get(i));
-      }
+      constantFill[i] =
+          TypeServices.VALUE_FILL_SERVICE
+              .call(Type.fromTsDataType(inputDataTypes.get(i)))
+              .create(filledValue, context.getZoneId());
     }
     return constantFill;
   }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/InColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/InColumnTransformer.java
index 5b3a6bf37db..7e640fd64d5 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/InColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/InColumnTransformer.java
@@ -19,32 +19,17 @@
 
 package org.apache.iotdb.calc.transformation.dag.column.unary;
 
-import org.apache.iotdb.calc.i18n.CalcMessages;
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
-import org.apache.iotdb.commons.exception.SemanticException;
+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.common.conf.TSFileConfig;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
-import org.apache.tsfile.utils.Binary;
 
-import java.util.HashSet;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 public class InColumnTransformer extends UnaryColumnTransformer {
-  private final Satisfy satisfy;
-
-  private final TypeEnum childType;
-
-  private Set<Integer> intSet;
-  private Set<Long> longSet;
-  private Set<Float> floatSet;
-  private Set<Double> doubleSet;
-  private Set<Boolean> booleanSet;
-  private Set<Binary> stringSet;
+  private final TypeServices.InColumnValueMatcher valueMatcher;
 
   public InColumnTransformer(
       Type returnType,
@@ -52,12 +37,15 @@ public class InColumnTransformer extends UnaryColumnTransformer {
       boolean isNotIn,
       Set<String> values) {
     super(returnType, childColumnTransformer);
-    satisfy = isNotIn ? new NotInSatisfy() : new InSatisfy();
-    this.childType =
-        childColumnTransformer.getType() == null
-            ? null
-            : childColumnTransformer.getType().getTypeEnum();
-    initTypedSet(values);
+    Type childType = childColumnTransformer.getType();
+    if (childType == null) {
+      valueMatcher = (column, position) -> false;
+      return;
+    }
+    TypeServices.InColumnValueMatcher matcher =
+        TypeServices.IN_COLUMN_VALUE_MATCHER_SERVICE.call(childType).create(values);
+    this.valueMatcher =
+        isNotIn ? (column, position) -> !matcher.matches(column, position) : matcher;
   }
 
   @Override
@@ -83,190 +71,6 @@ public class InColumnTransformer extends UnaryColumnTransformer {
   }
 
   private void transform(Column column, ColumnBuilder columnBuilder, int i) {
-    switch (childType) {
-      case INT32:
-      case DATE:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getInt(i)));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getLong(i)));
-        break;
-      case FLOAT:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getFloat(i)));
-        break;
-      case DOUBLE:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getDouble(i)));
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getBoolean(i)));
-        break;
-      case STRING:
-      case TEXT:
-      case BLOB:
-        returnType.writeBoolean(columnBuilder, satisfy.of(column.getBinary(i)));
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            CalcMessages.UNSUPPORTED_DATA_TYPE_LOWER + childType);
-    }
-  }
-
-  private void initTypedSet(Set<String> values) {
-    if (childType == null) {
-      return;
-    }
-    String errorMsg = "\"%s\" cannot be cast to [%s]";
-    switch (childType) {
-      case INT32:
-        intSet = new HashSet<>();
-        for (String value : values) {
-          try {
-            intSet.add(Integer.valueOf(value));
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        break;
-      case INT64:
-      case TIMESTAMP:
-        longSet = new HashSet<>();
-        for (String value : values) {
-          try {
-            longSet.add(Long.valueOf(value));
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        break;
-      case FLOAT:
-        floatSet = new HashSet<>();
-        for (String value : values) {
-          try {
-            floatSet.add(Float.valueOf(value));
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        break;
-      case DOUBLE:
-        doubleSet = new HashSet<>();
-        for (String value : values) {
-          try {
-            doubleSet.add(Double.valueOf(value));
-          } catch (IllegalArgumentException e) {
-            throw new SemanticException(String.format(errorMsg, value, childType));
-          }
-        }
-        break;
-      case BOOLEAN:
-        booleanSet = new HashSet<>();
-        for (String value : values) {
-          booleanSet.add(strictCastToBool(value));
-        }
-        break;
-      case TEXT:
-      case STRING:
-        stringSet =
-            values.stream()
-                .map(v -> new Binary(v, TSFileConfig.STRING_CHARSET))
-                .collect(Collectors.toSet());
-        break;
-      case BLOB:
-      case DATE:
-      default:
-        throw new UnsupportedOperationException(
-            CalcMessages.UNSUPPORTED_DATA_TYPE_LOWER + childType);
-    }
-  }
-
-  private boolean strictCastToBool(String s) {
-    if ("true".equalsIgnoreCase(s)) {
-      return true;
-    } else if ("false".equalsIgnoreCase(s)) {
-      return false;
-    }
-    throw new SemanticException(String.format(CalcMessages.CANNOT_CAST_TO_BOOLEAN, s));
-  }
-
-  private interface Satisfy {
-
-    boolean of(int intValue);
-
-    boolean of(long longValue);
-
-    boolean of(float floatValue);
-
-    boolean of(double doubleValue);
-
-    boolean of(boolean booleanValue);
-
-    boolean of(Binary stringValue);
-  }
-
-  private class InSatisfy implements Satisfy {
-
-    @Override
-    public boolean of(int intValue) {
-      return intSet.contains(intValue);
-    }
-
-    @Override
-    public boolean of(long longValue) {
-      return longSet.contains(longValue);
-    }
-
-    @Override
-    public boolean of(float floatValue) {
-      return floatSet.contains(floatValue);
-    }
-
-    @Override
-    public boolean of(double doubleValue) {
-      return doubleSet.contains(doubleValue);
-    }
-
-    @Override
-    public boolean of(boolean booleanValue) {
-      return booleanSet.contains(booleanValue);
-    }
-
-    @Override
-    public boolean of(Binary stringValue) {
-      return stringSet.contains(stringValue);
-    }
-  }
-
-  private class NotInSatisfy implements Satisfy {
-
-    @Override
-    public boolean of(int intValue) {
-      return !intSet.contains(intValue);
-    }
-
-    @Override
-    public boolean of(long longValue) {
-      return !longSet.contains(longValue);
-    }
-
-    @Override
-    public boolean of(float floatValue) {
-      return !floatSet.contains(floatValue);
-    }
-
-    @Override
-    public boolean of(double doubleValue) {
-      return !doubleSet.contains(doubleValue);
-    }
-
-    @Override
-    public boolean of(boolean booleanValue) {
-      return !booleanSet.contains(booleanValue);
-    }
-
-    @Override
-    public boolean of(Binary stringValue) {
-      return !stringSet.contains(stringValue);
-    }
+    returnType.writeBoolean(columnBuilder, valueMatcher.matches(column, i));
   }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/AbstractCastFunctionColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/AbstractCastFunctionColumnTransformer.java
index d8df707f39c..caafaef07b8 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/AbstractCastFunctionColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/AbstractCastFunctionColumnTransformer.java
@@ -21,43 +21,33 @@ package org.apache.iotdb.calc.transformation.dag.column.unary.scalar;
 
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.unary.UnaryColumnTransformer;
-import org.apache.iotdb.calc.transformation.dag.util.CastFunctionUtils;
-import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
-import org.apache.iotdb.commons.exception.SemanticException;
-import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils;
+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.common.conf.TSFileConfig;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
-import org.apache.tsfile.utils.Binary;
-import org.apache.tsfile.utils.BytesUtils;
-import org.apache.tsfile.utils.DateUtils;
 
 import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
-
-import static org.apache.iotdb.calc.transformation.dag.util.CastFunctionUtils.ERROR_MSG;
-import static org.apache.iotdb.rpc.TSStatusCode.DATE_OUT_OF_RANGE;
 
 public abstract class AbstractCastFunctionColumnTransformer extends UnaryColumnTransformer {
 
   private final ZoneId zoneId;
+  private final TypeServices.CastInputService castInputService;
+  private final TypeServices.CastValueService castValueService;
 
   protected AbstractCastFunctionColumnTransformer(
       Type returnType, ColumnTransformer childColumnTransformer, ZoneId zoneId) {
     super(returnType, childColumnTransformer);
     this.zoneId = zoneId;
+    this.castInputService = TypeServices.CAST_INPUT_SERVICE.call(childColumnTransformer.getType());
+    this.castValueService = TypeServices.CAST_VALUE_SERVICE.call(returnType);
   }
 
   @Override
   protected void doTransform(Column column, ColumnBuilder columnBuilder) {
-    Type childType = childColumnTransformer.getType();
-    TypeEnum sourceType = childType.getTypeEnum();
     for (int i = 0, n = column.getPositionCount(); i < n; i++) {
       if (!column.isNull(i)) {
-        transform(column, columnBuilder, sourceType, childType, i);
+        transform(column, columnBuilder, i);
       } else {
         columnBuilder.appendNull();
       }
@@ -66,341 +56,18 @@ public abstract class AbstractCastFunctionColumnTransformer extends UnaryColumnT
 
   @Override
   protected void doTransform(Column column, ColumnBuilder columnBuilder, boolean[] selection) {
-    Type childType = childColumnTransformer.getType();
-    TypeEnum sourceType = childType.getTypeEnum();
     for (int i = 0, n = column.getPositionCount(); i < n; i++) {
       if (selection[i] && !column.isNull(i)) {
-        transform(column, columnBuilder, sourceType, childType, i);
+        transform(column, columnBuilder, i);
       } else {
         columnBuilder.appendNull();
       }
     }
   }
 
-  protected abstract void transform(
-      Column column, ColumnBuilder columnBuilder, TypeEnum sourceType, Type childType, int i);
-
-  protected void cast(ColumnBuilder columnBuilder, int value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, value);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeLong(columnBuilder, value);
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, value);
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value != 0);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.intToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void castDate(ColumnBuilder columnBuilder, int value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, value);
-        break;
-      case INT64:
-        returnType.writeLong(columnBuilder, value);
-        break;
-      case TIMESTAMP:
-        returnType.writeLong(
-            columnBuilder,
-            DateTimeUtils.correctPrecision(DateUtils.parseIntToTimestamp(value, zoneId)));
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, value);
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value != 0);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(DateUtils.formatDate(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.intToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void castTimestamp(ColumnBuilder columnBuilder, long value) {
-    try {
-      switch (returnType.getTypeEnum()) {
-        case INT32:
-          returnType.writeInt(columnBuilder, (CastFunctionUtils.castLongToInt(value)));
-          break;
-        case DATE:
-          returnType.writeInt(
-              columnBuilder,
-              DateUtils.parseDateExpressionToInt(DateTimeUtils.convertToLocalDate(value, zoneId)));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          returnType.writeLong(columnBuilder, value);
-          break;
-        case FLOAT:
-          returnType.writeFloat(columnBuilder, value);
-          break;
-        case DOUBLE:
-          returnType.writeDouble(columnBuilder, value);
-          break;
-        case BOOLEAN:
-          returnType.writeBoolean(columnBuilder, value != 0L);
-          break;
-        case TEXT:
-        case STRING:
-          returnType.writeBinary(
-              columnBuilder, BytesUtils.valueOf(DateTimeUtils.convertLongToDate(value, zoneId)));
-          break;
-        case BLOB:
-          returnType.writeBinary(columnBuilder, new Binary(BytesUtils.longToBytes(value)));
-          break;
-        default:
-          throw new UnsupportedOperationException(
-              String.format(ERROR_MSG, returnType.getTypeEnum()));
-      }
-    } catch (DateTimeParseException e) {
-      throw new IoTDBRuntimeException(
-          "Year must be between 1000 and 9999.", DATE_OUT_OF_RANGE.getStatusCode(), true);
-    }
-  }
-
-  protected void cast(ColumnBuilder columnBuilder, long value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, (CastFunctionUtils.castLongToInt(value)));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeLong(columnBuilder, value);
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, value);
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value != 0L);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.longToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void cast(ColumnBuilder columnBuilder, float value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, CastFunctionUtils.castFloatToInt(value));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeLong(columnBuilder, CastFunctionUtils.castFloatToLong(value));
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, value);
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value != 0.0f);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.floatToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void cast(ColumnBuilder columnBuilder, double value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, CastFunctionUtils.castDoubleToInt(value));
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeLong(columnBuilder, CastFunctionUtils.castDoubleToLong(value));
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, CastFunctionUtils.castDoubleToFloat(value));
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value != 0.0);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.doubleToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void cast(ColumnBuilder columnBuilder, boolean value) {
-    switch (returnType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        returnType.writeInt(columnBuilder, value ? 1 : 0);
-        break;
-      case INT64:
-      case TIMESTAMP:
-        returnType.writeLong(columnBuilder, value ? 1L : 0);
-        break;
-      case FLOAT:
-        returnType.writeFloat(columnBuilder, value ? 1.0f : 0);
-        break;
-      case DOUBLE:
-        returnType.writeDouble(columnBuilder, value ? 1.0 : 0);
-        break;
-      case BOOLEAN:
-        returnType.writeBoolean(columnBuilder, value);
-        break;
-      case TEXT:
-      case STRING:
-        returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
-        break;
-      case BLOB:
-        returnType.writeBinary(columnBuilder, new Binary(BytesUtils.boolToBytes(value)));
-        break;
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
-  }
-
-  protected void castString(ColumnBuilder columnBuilder, Binary value) {
-    String stringValue = value.getStringValue(TSFileConfig.STRING_CHARSET);
-    try {
-      switch (returnType.getTypeEnum()) {
-        case INT32:
-          returnType.writeInt(columnBuilder, Integer.parseInt(stringValue));
-          break;
-        case DATE:
-          returnType.writeInt(columnBuilder, DateUtils.parseDateExpressionToInt(stringValue));
-          break;
-        case INT64:
-          returnType.writeLong(columnBuilder, Long.parseLong(stringValue));
-          break;
-        case TIMESTAMP:
-          returnType.writeLong(
-              columnBuilder, DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId));
-          break;
-        case FLOAT:
-          returnType.writeFloat(columnBuilder, CastFunctionUtils.castTextToFloat(stringValue));
-          break;
-        case DOUBLE:
-          returnType.writeDouble(columnBuilder, CastFunctionUtils.castTextToDouble(stringValue));
-          break;
-        case BOOLEAN:
-          returnType.writeBoolean(columnBuilder, CastFunctionUtils.castTextToBoolean(stringValue));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          returnType.writeBinary(columnBuilder, value);
-          break;
-        default:
-          throw new UnsupportedOperationException(
-              String.format(ERROR_MSG, returnType.getTypeEnum()));
-      }
-    } catch (DateTimeParseException | NumberFormatException e) {
-      throw new SemanticException(
-          String.format("Cannot cast %s to %s type", stringValue, returnType.getDisplayName()));
-    }
-  }
-
-  protected void castBlob(ColumnBuilder columnBuilder, Binary value) {
-    String stringValue = BytesUtils.parseBlobByteArrayToString(value.getValues());
-    try {
-      switch (returnType.getTypeEnum()) {
-        case INT32:
-          returnType.writeInt(columnBuilder, Integer.parseInt(stringValue));
-          break;
-        case DATE:
-          returnType.writeInt(columnBuilder, DateUtils.parseDateExpressionToInt(stringValue));
-          break;
-        case INT64:
-          returnType.writeLong(columnBuilder, Long.parseLong(stringValue));
-          break;
-        case TIMESTAMP:
-          returnType.writeLong(
-              columnBuilder, DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId));
-          break;
-        case FLOAT:
-          returnType.writeFloat(columnBuilder, CastFunctionUtils.castTextToFloat(stringValue));
-          break;
-        case DOUBLE:
-          returnType.writeDouble(columnBuilder, CastFunctionUtils.castTextToDouble(stringValue));
-          break;
-        case BOOLEAN:
-          returnType.writeBoolean(columnBuilder, CastFunctionUtils.castTextToBoolean(stringValue));
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-          returnType.writeBinary(columnBuilder, value);
-          break;
-        default:
-          throw new UnsupportedOperationException(
-              String.format(ERROR_MSG, returnType.getTypeEnum()));
-      }
-    } catch (DateTimeParseException | NumberFormatException e) {
-      throw new SemanticException(
-          String.format("Cannot cast %s to %s type", stringValue, returnType.getDisplayName()));
-    }
-  }
+  protected abstract void transform(Column column, ColumnBuilder columnBuilder, int i);
 
-  protected void castObject(ColumnBuilder columnBuilder, Binary value) {
-    String stringValue = BytesUtils.parseObjectByteArrayToString(value.getValues());
-    if (returnType.getTypeEnum() == TypeEnum.STRING) {
-      returnType.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(stringValue)));
-    } else {
-      throw new UnsupportedOperationException(String.format(ERROR_MSG, returnType.getTypeEnum()));
-    }
+  protected void castSourceValue(Column column, ColumnBuilder columnBuilder, int position) {
+    castInputService.cast(column, columnBuilder, position, castValueService, zoneId);
   }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/CastFunctionColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/CastFunctionColumnTransformer.java
index 9a86020a183..4882dbb8653 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/CastFunctionColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/CastFunctionColumnTransformer.java
@@ -24,7 +24,6 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 
 import java.time.ZoneId;
 
@@ -36,44 +35,7 @@ public class CastFunctionColumnTransformer extends AbstractCastFunctionColumnTra
   }
 
   @Override
-  protected void transform(
-      Column column, ColumnBuilder columnBuilder, TypeEnum sourceType, Type childType, int i) {
-    switch (sourceType) {
-      case INT32:
-        cast(columnBuilder, childType.getInt(column, i));
-        break;
-      case DATE:
-        castDate(columnBuilder, childType.getInt(column, i));
-        break;
-      case INT64:
-        cast(columnBuilder, childType.getLong(column, i));
-        break;
-      case TIMESTAMP:
-        castTimestamp(columnBuilder, childType.getLong(column, i));
-        break;
-      case FLOAT:
-        cast(columnBuilder, childType.getFloat(column, i));
-        break;
-      case DOUBLE:
-        cast(columnBuilder, childType.getDouble(column, i));
-        break;
-      case BOOLEAN:
-        cast(columnBuilder, childType.getBoolean(column, i));
-        break;
-      case TEXT:
-      case STRING:
-        castString(columnBuilder, childType.getBinary(column, i));
-        break;
-      case BLOB:
-        castBlob(columnBuilder, childType.getBinary(column, i));
-        break;
-      case OBJECT:
-        castObject(columnBuilder, childType.getBinary(column, i));
-        break;
-      default:
-        throw new UnsupportedOperationException(
-            String.format(
-                "Unsupported source dataType: %s", childColumnTransformer.getType().getTypeEnum()));
-    }
+  protected void transform(Column column, ColumnBuilder columnBuilder, int i) {
+    castSourceValue(column, columnBuilder, i);
   }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/FormatColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/FormatColumnTransformer.java
index 1fe62b0759a..d8bb36a46b1 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/FormatColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/FormatColumnTransformer.java
@@ -21,15 +21,13 @@ package org.apache.iotdb.calc.transformation.dag.column.unary.scalar;
 
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.MultiColumnTransformer;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.exception.SemanticException;
-import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 import org.apache.tsfile.utils.Binary;
-import org.apache.tsfile.utils.DateUtils;
 
 import java.time.ZoneId;
 import java.util.IllegalFormatConversionException;
@@ -42,11 +40,18 @@ import static org.apache.iotdb.pipe.api.type.Binary.stringToBytes;
 public class FormatColumnTransformer extends MultiColumnTransformer {
 
   private final ZoneId zoneId;
+  private final TypeServices.FormatValueConverter[] valueConverters;
 
   public FormatColumnTransformer(
       Type returnType, List<ColumnTransformer> columnTransformerList, ZoneId zoneId) {
     super(returnType, columnTransformerList);
     this.zoneId = zoneId;
+    this.valueConverters = new TypeServices.FormatValueConverter[columnTransformerList.size() - 1];
+    for (int i = 0; i < valueConverters.length; i++) {
+      valueConverters[i] =
+          TypeServices.FORMAT_VALUE_CONVERTER_SERVICE.call(
+              columnTransformerList.get(i + 1).getType());
+    }
   }
 
   @Override
@@ -75,11 +80,10 @@ public class FormatColumnTransformer extends MultiColumnTransformer {
     String pattern = String.valueOf(childrenColumns.get(0).getBinary(i));
     for (int j = 0; j < valueColumns.size(); j++) {
       Column column = valueColumns.get(j);
-      TypeEnum type = columnTransformerList.get(j + 1).getType().getTypeEnum();
       if (column.isNull(i)) {
         values[j] = null;
       } else {
-        values[j] = valueConverter(type, column, i);
+        values[j] = valueConverters[j].convert(column, i, zoneId);
       }
     }
     try {
@@ -92,29 +96,6 @@ public class FormatColumnTransformer extends MultiColumnTransformer {
     }
   }
 
-  private Object valueConverter(TypeEnum type, Column column, int i) {
-    switch (type) {
-      case UNKNOWN:
-        return null;
-      case INT32:
-      case INT64:
-      case FLOAT:
-      case DOUBLE:
-      case BOOLEAN:
-      case TEXT:
-      case STRING:
-      case BLOB:
-        return column.getObject(i);
-      case DATE:
-        return DateUtils.parseIntToLocalDate(column.getInt(i));
-      case TIMESTAMP:
-        return DateTimeUtils.convertToZonedDateTime(column.getLong(i), zoneId);
-      default:
-        throw new UnsupportedOperationException(
-            String.format("Unsupported source dataType: %s", type));
-    }
-  }
-
   @Override
   protected void checkType() {
     // do nothing because the type is checked in tableMetaDataImpl
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
index 08c170e45a6..5a1d8a00225 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
@@ -26,7 +26,6 @@ import org.apache.iotdb.calc.transformation.dag.column.binary.BinaryColumnTransf
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 
 public class RoundColumnTransformer extends BinaryColumnTransformer {
 
@@ -38,7 +37,7 @@ public class RoundColumnTransformer extends BinaryColumnTransformer {
   @Override
   protected void doTransform(
       Column leftColumn, Column rightColumn, ColumnBuilder builder, int positionCount) {
-    TypeEnum sourceType = leftTransformer.getType().getTypeEnum();
+    Type sourceType = leftTransformer.getType();
     for (int i = 0; i < positionCount; i++) {
       if (!leftColumn.isNull(i) && !rightColumn.isNull(i)) {
         transform(sourceType, leftColumn, rightColumn, builder, i);
@@ -55,7 +54,7 @@ public class RoundColumnTransformer extends BinaryColumnTransformer {
       ColumnBuilder builder,
       int positionCount,
       boolean[] selection) {
-    TypeEnum sourceType = leftTransformer.getType().getTypeEnum();
+    Type sourceType = leftTransformer.getType();
     for (int i = 0; i < positionCount; i++) {
       if (selection[i] && !leftColumn.isNull(i) && !rightColumn.isNull(i)) {
         transform(sourceType, leftColumn, rightColumn, builder, i);
@@ -66,35 +65,10 @@ public class RoundColumnTransformer extends BinaryColumnTransformer {
   }
 
   private void transform(
-      TypeEnum sourceType, Column leftColumn, Column rightColumn, ColumnBuilder builder, int i) {
+      Type sourceType, Column leftColumn, Column rightColumn, ColumnBuilder builder, int i) {
     int places = rightTransformer.getType().getInt(rightColumn, i);
-    switch (sourceType) {
-      case INT32:
-        builder.writeDouble(
-            Math.rint(leftColumn.getInt(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case INT64:
-        builder.writeDouble(
-            Math.rint(leftColumn.getLong(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case FLOAT:
-        builder.writeDouble(
-            Math.rint(leftColumn.getFloat(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case DOUBLE:
-        builder.writeDouble(
-            Math.rint(leftColumn.getDouble(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case DATE:
-      case TEXT:
-      case BOOLEAN:
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      default:
-        throw new UnsupportedOperationException(
-            String.format("Unsupported source dataType: %s", sourceType));
-    }
+    double scale = Math.pow(10, places);
+    builder.writeDouble(Math.rint(sourceType.getDouble(leftColumn, i) * scale) / scale);
   }
 
   @Override
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundFunctionColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundFunctionColumnTransformer.java
index 4a429c1f62e..8104c4e4c0a 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundFunctionColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/RoundFunctionColumnTransformer.java
@@ -25,7 +25,6 @@ import org.apache.iotdb.calc.transformation.dag.column.unary.UnaryColumnTransfor
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 
 public class RoundFunctionColumnTransformer extends UnaryColumnTransformer {
 
@@ -39,7 +38,7 @@ public class RoundFunctionColumnTransformer extends UnaryColumnTransformer {
 
   @Override
   protected void doTransform(Column column, ColumnBuilder columnBuilder) {
-    TypeEnum sourceType = childColumnTransformer.getType().getTypeEnum();
+    Type sourceType = childColumnTransformer.getType();
     for (int i = 0, n = column.getPositionCount(); i < n; i++) {
       if (!column.isNull(i)) {
         transform(column, columnBuilder, sourceType, i);
@@ -51,7 +50,7 @@ public class RoundFunctionColumnTransformer extends UnaryColumnTransformer {
 
   @Override
   protected void doTransform(Column column, ColumnBuilder columnBuilder, boolean[] selection) {
-    TypeEnum sourceType = childColumnTransformer.getType().getTypeEnum();
+    Type sourceType = childColumnTransformer.getType();
     for (int i = 0, n = column.getPositionCount(); i < n; i++) {
       if (selection[i] && !column.isNull(i)) {
         transform(column, columnBuilder, sourceType, i);
@@ -61,34 +60,8 @@ public class RoundFunctionColumnTransformer extends UnaryColumnTransformer {
     }
   }
 
-  private void transform(Column column, ColumnBuilder columnBuilder, TypeEnum sourceType, int i) {
-    switch (sourceType) {
-      case INT32:
-        columnBuilder.writeDouble(
-            Math.rint(column.getInt(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case INT64:
-        columnBuilder.writeDouble(
-            Math.rint(column.getLong(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case FLOAT:
-        columnBuilder.writeDouble(
-            Math.rint(column.getFloat(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case DOUBLE:
-        columnBuilder.writeDouble(
-            Math.rint(column.getDouble(i) * Math.pow(10, places)) / Math.pow(10, places));
-        break;
-      case TIMESTAMP:
-      case STRING:
-      case BOOLEAN:
-      case BLOB:
-      case TEXT:
-      case DATE:
-      default:
-        throw new UnsupportedOperationException(
-            String.format(
-                "Unsupported source dataType: %s", childColumnTransformer.getType().getTypeEnum()));
-    }
+  private void transform(Column column, ColumnBuilder columnBuilder, Type sourceType, int i) {
+    double scale = Math.pow(10, places);
+    columnBuilder.writeDouble(Math.rint(sourceType.getDouble(column, i) * scale) / scale);
   }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/TryCastFunctionColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/TryCastFunctionColumnTransformer.java
index 9d07564640b..89676316b11 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/TryCastFunctionColumnTransformer.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/TryCastFunctionColumnTransformer.java
@@ -25,7 +25,6 @@ import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.read.common.type.Type;
-import org.apache.tsfile.read.common.type.TypeEnum;
 
 import java.time.ZoneId;
 
@@ -37,47 +36,9 @@ public class TryCastFunctionColumnTransformer extends AbstractCastFunctionColumn
   }
 
   @Override
-  protected void transform(
-      Column column, ColumnBuilder columnBuilder, TypeEnum sourceType, Type childType, int i) {
+  protected void transform(Column column, ColumnBuilder columnBuilder, int i) {
     try {
-      switch (sourceType) {
-        case INT32:
-          cast(columnBuilder, childType.getInt(column, i));
-          break;
-        case DATE:
-          castDate(columnBuilder, childType.getInt(column, i));
-          break;
-        case INT64:
-          cast(columnBuilder, childType.getLong(column, i));
-          break;
-        case TIMESTAMP:
-          castTimestamp(columnBuilder, childType.getLong(column, i));
-          break;
-        case FLOAT:
-          cast(columnBuilder, childType.getFloat(column, i));
-          break;
-        case DOUBLE:
-          cast(columnBuilder, childType.getDouble(column, i));
-          break;
-        case BOOLEAN:
-          cast(columnBuilder, childType.getBoolean(column, i));
-          break;
-        case TEXT:
-        case STRING:
-          castString(columnBuilder, childType.getBinary(column, i));
-          break;
-        case BLOB:
-          castBlob(columnBuilder, childType.getBinary(column, i));
-          break;
-        case OBJECT:
-          castObject(columnBuilder, childType.getBinary(column, i));
-          break;
-        default:
-          throw new UnsupportedOperationException(
-              String.format(
-                  "Unsupported source dataType: %s",
-                  childColumnTransformer.getType().getTypeEnum()));
-      }
+      castSourceValue(column, columnBuilder, i);
     } catch (IoTDBRuntimeException e) {
       columnBuilder.appendNull();
     }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java
index 98c00c1ee89..9329daec971 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java
@@ -20,20 +20,15 @@
 package org.apache.iotdb.calc.transformation.dag.util;
 
 import org.apache.iotdb.calc.i18n.CalcMessages;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.exception.SemanticException;
 import org.apache.iotdb.commons.queryengine.common.SessionInfo;
-import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils;
 
-import org.apache.tsfile.common.conf.TSFileConfig;
 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 javax.annotation.Nonnull;
 
 import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
 
 public class CastFunctionUtils {
   public static final String ERROR_MSG = "Unsupported target dataType: %s";
@@ -119,238 +114,11 @@ public class CastFunctionUtils {
   // used by IrExpressionInterpreter to do constant folding
   public static Object cast(
       @Nonnull Object value, Type sourceType, Type targetType, SessionInfo session) {
-    switch (sourceType.getTypeEnum()) {
-      case INT32:
-        int intV = value instanceof Integer ? (int) value : ((Long) value).intValue();
-        return castInt(intV, targetType);
-      case DATE:
-        int dateV = value instanceof Integer ? (int) value : ((Long) value).intValue();
-        return castDate(dateV, targetType, session.getZoneId());
-      case INT64:
-        long longV = (Long) value;
-        return castLong(longV, targetType);
-      case TIMESTAMP:
-        long timestampV = (Long) value;
-        return castTimestamp(timestampV, targetType, session.getZoneId());
-      case FLOAT:
-        float floatV = value instanceof Float ? (float) value : ((Double) value).floatValue();
-        return castFloat(floatV, targetType);
-      case DOUBLE:
-        double doubleV = (Double) value;
-        return castDouble(doubleV, targetType);
-      case BOOLEAN:
-        boolean boolV = (Boolean) value;
-        return castBool(boolV, targetType);
-      case TEXT:
-      case STRING:
-      case BLOB:
-        Binary binaryV = (Binary) value;
-        return castBinary(binaryV, targetType, session.getZoneId());
-      default:
-        throw new UnsupportedOperationException(
-            String.format("Unsupported source dataType: %s", sourceType.getTypeEnum()));
-    }
-  }
-
-  private static Object castInt(int value, Type targetType) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return value;
-      case INT64:
-      case TIMESTAMP:
-        return (long) value;
-      case FLOAT:
-        return (float) value;
-      case DOUBLE:
-        return (double) value;
-      case BOOLEAN:
-        return value != 0;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(String.valueOf(value));
-      case BLOB:
-        return new Binary(BytesUtils.intToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castDate(int value, Type targetType, ZoneId zoneId) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return value;
-      case INT64:
-        return (long) value;
-      case TIMESTAMP:
-        return DateTimeUtils.correctPrecision(DateUtils.parseIntToTimestamp(value, zoneId));
-      case FLOAT:
-        return (float) value;
-      case DOUBLE:
-        return (double) value;
-      case BOOLEAN:
-        return value != 0;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(DateUtils.formatDate(value));
-      case BLOB:
-        return new Binary(BytesUtils.intToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castLong(long value, Type targetType) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return castLongToInt(value);
-      case INT64:
-      case TIMESTAMP:
-        return value;
-      case FLOAT:
-        return (float) value;
-      case DOUBLE:
-        return (double) value;
-      case BOOLEAN:
-        return value != 0L;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(String.valueOf(value));
-      case BLOB:
-        return new Binary(BytesUtils.longToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castTimestamp(long value, Type targetType, ZoneId zoneId) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-        return castLongToInt(value);
-      case DATE:
-        return DateUtils.parseDateExpressionToInt(DateTimeUtils.convertToLocalDate(value, zoneId));
-      case INT64:
-      case TIMESTAMP:
-        return value;
-      case FLOAT:
-        return (float) value;
-      case DOUBLE:
-        return (double) value;
-      case BOOLEAN:
-        return value != 0L;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(DateTimeUtils.convertLongToDate(value, zoneId));
-      case BLOB:
-        return new Binary(BytesUtils.longToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castFloat(float value, Type targetType) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return castFloatToInt(value);
-      case INT64:
-      case TIMESTAMP:
-        return castFloatToLong(value);
-      case FLOAT:
-        return value;
-      case DOUBLE:
-        return (double) value;
-      case BOOLEAN:
-        return value != 0.0f;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(String.valueOf(value));
-      case BLOB:
-        return new Binary(BytesUtils.floatToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castDouble(double value, Type targetType) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return castDoubleToInt(value);
-      case INT64:
-      case TIMESTAMP:
-        return castDoubleToLong(value);
-      case FLOAT:
-        return castDoubleToFloat(value);
-      case DOUBLE:
-        return value;
-      case BOOLEAN:
-        return value != 0.0d;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(String.valueOf(value));
-      case BLOB:
-        return new Binary(BytesUtils.doubleToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castBool(boolean value, Type targetType) {
-    switch (targetType.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return value ? 1 : 0;
-      case INT64:
-      case TIMESTAMP:
-        return value ? 1L : 0L;
-      case FLOAT:
-        return value ? 1.0f : 0.0f;
-      case DOUBLE:
-        return value ? 1.0d : 0.0d;
-      case BOOLEAN:
-        return value;
-      case TEXT:
-      case STRING:
-        return BytesUtils.valueOf(String.valueOf(value));
-      case BLOB:
-        return new Binary(BytesUtils.boolToBytes(value));
-      default:
-        throw new UnsupportedOperationException(String.format(ERROR_MSG, targetType.getTypeEnum()));
-    }
-  }
-
-  private static Object castBinary(Binary value, Type targetType, ZoneId zoneId) {
-    String stringValue = value.getStringValue(TSFileConfig.STRING_CHARSET);
-    try {
-      switch (targetType.getTypeEnum()) {
-        case INT32:
-          return Integer.parseInt(stringValue);
-        case DATE:
-          return DateUtils.parseDateExpressionToInt(stringValue);
-        case INT64:
-          return Long.parseLong(stringValue);
-        case TIMESTAMP:
-          return DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId);
-        case FLOAT:
-          return castTextToFloat(stringValue);
-        case DOUBLE:
-          return castTextToDouble(stringValue);
-        case BOOLEAN:
-          return castTextToBoolean(stringValue);
-        case TEXT:
-        case STRING:
-        case BLOB:
-          return value;
-        default:
-          throw new UnsupportedOperationException(
-              String.format(ERROR_MSG, targetType.getTypeEnum()));
-      }
-    } catch (DateTimeParseException | NumberFormatException e) {
-      throw new SemanticException(
-          String.format("Cannot cast %s to %s type", stringValue, targetType.getDisplayName()));
-    }
+    TypeServices.CastObjectInputService inputService =
+        TypeServices.CAST_OBJECT_INPUT_SERVICE.call(sourceType);
+    TypeServices.CastObjectValueService valueService =
+        TypeServices.CAST_OBJECT_VALUE_SERVICE.call(targetType);
+    ZoneId zoneId = session == null ? null : session.getZoneId();
+    return inputService.cast(value, valueService, zoneId);
   }
 }
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 63c5eaaa249..9152da5d0ab 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
@@ -22,6 +22,13 @@ package org.apache.iotdb.calc.utils;
 import org.apache.iotdb.calc.execution.operator.process.fill.IFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.IFillFilter;
 import org.apache.iotdb.calc.execution.operator.process.fill.ILinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.BinaryConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.BooleanConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.DoubleConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.FloatConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.IntConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.constant.LongConstantFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.identity.IdentityFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.identity.IdentityLinearFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.linear.DoubleLinearFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.linear.FloatLinearFill;
@@ -60,9 +67,27 @@ import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.De
 import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator;
 import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition;
 import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AbstractApproxPercentileAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.AggregationMask;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.BinaryApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.BlobApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.BooleanApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.DoubleApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.FloatApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.IntApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.LongApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.TableAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.AbstractGroupedApproxPercentileAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BinaryGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BlobGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.BooleanGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.DoubleGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.FloatGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedMaxMinByBaseAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.IntGroupedApproxMostFrequentAccumulator;
+import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.LongGroupedApproxMostFrequentAccumulator;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BinaryBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.BooleanBigArray;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.DoubleBigArray;
@@ -70,6 +95,15 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.gr
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.IntBigArray;
 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.plan.relational.planner.CastToBlobLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToBooleanLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToDateLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToDoubleLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToFloatLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToInt32LiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToInt64LiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToStringLiteralVisitor;
+import org.apache.iotdb.calc.plan.relational.planner.CastToTimestampLiteralVisitor;
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.BinaryGreatestColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.BinaryLeastColumnTransformer;
@@ -79,14 +113,34 @@ import org.apache.iotdb.calc.transformation.dag.column.multi.DoubleGreatestColum
 import org.apache.iotdb.calc.transformation.dag.column.multi.DoubleLeastColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.FloatGreatestColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.FloatLeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InBinaryMultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InBooleanMultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InDoubleMultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InFloatMultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InInt32MultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InInt64MultiColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.column.multi.InMultiColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.Int32GreatestColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.Int32LeastColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.Int64GreatestColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.multi.Int64LeastColumnTransformer;
+import org.apache.iotdb.calc.transformation.dag.util.CastFunctionUtils;
 import org.apache.iotdb.calc.utils.datastructure.SortKey;
+import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
+import org.apache.iotdb.commons.exception.SemanticException;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BinaryLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.DoubleLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.FloatLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.GenericLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Literal;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral;
+import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils;
 
 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.file.metadata.enums.TSEncoding;
 import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.read.common.type.service.TypeService;
@@ -97,13 +151,19 @@ import org.apache.tsfile.utils.ReadWriteIOUtils;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
 import java.util.function.IntUnaryOperator;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 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;
@@ -111,6 +171,7 @@ import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtil
 public class TypeServices {
 
   private static final IdentityLinearFill IDENTITY_LINEAR_FILL = new IdentityLinearFill();
+  private static final IdentityFill IDENTITY_FILL = new IdentityFill();
 
   // Unsupported fill types defer their exception until the returned strategy is used so check()
   // can still validate that every TypeEnum has a service entry.
@@ -290,6 +351,545 @@ public class TypeServices {
                     throw new UnSupportedDataTypeException(type.toString()).setChecked(true);
               };
 
+  public static final TypeService<ValueFillFactory> VALUE_FILL_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                (literal, zoneId) -> {
+                  Boolean value = literal.accept(new CastToBooleanLiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new BooleanConstantFill(value);
+                };
+            case TEXT, STRING ->
+                (literal, zoneId) -> {
+                  Binary value =
+                      literal.accept(
+                          new CastToStringLiteralVisitor(TSFileConfig.STRING_CHARSET), null);
+                  return value == null ? IDENTITY_FILL : new BinaryConstantFill(value);
+                };
+            case BLOB ->
+                (literal, zoneId) -> {
+                  Binary value = literal.accept(new CastToBlobLiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new BinaryConstantFill(value);
+                };
+            case INT32 ->
+                (literal, zoneId) -> {
+                  Integer value = literal.accept(new CastToInt32LiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new IntConstantFill(value);
+                };
+            case DATE ->
+                (literal, zoneId) -> {
+                  Integer value = literal.accept(new CastToDateLiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new IntConstantFill(value);
+                };
+            case INT64 ->
+                (literal, zoneId) -> {
+                  Long value = literal.accept(new CastToInt64LiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new LongConstantFill(value);
+                };
+            case TIMESTAMP ->
+                (literal, zoneId) -> {
+                  Long value = literal.accept(new CastToTimestampLiteralVisitor(zoneId), null);
+                  return value == null ? IDENTITY_FILL : new LongConstantFill(value);
+                };
+            case FLOAT ->
+                (literal, zoneId) -> {
+                  Float value = literal.accept(new CastToFloatLiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new FloatConstantFill(value);
+                };
+            case DOUBLE ->
+                (literal, zoneId) -> {
+                  Double value = literal.accept(new CastToDoubleLiteralVisitor(), null);
+                  return value == null ? IDENTITY_FILL : new DoubleConstantFill(value);
+                };
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (literal, zoneId) -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  /** Reads values for a cast using the source type's representation. */
+  public static final TypeService<CastInputService> CAST_INPUT_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castInt(columnBuilder, type.getInt(column, position));
+            case DATE ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castDate(columnBuilder, type.getInt(column, position), zoneId);
+            case INT64 ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castLong(columnBuilder, type.getLong(column, position));
+            case TIMESTAMP ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castTimestamp(
+                        columnBuilder, type.getLong(column, position), zoneId);
+            case FLOAT ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castFloat(columnBuilder, type.getFloat(column, position));
+            case DOUBLE ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castDouble(columnBuilder, type.getDouble(column, position));
+            case BOOLEAN ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castBoolean(columnBuilder, type.getBoolean(column, position));
+            case TEXT, STRING ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castString(
+                        columnBuilder, type.getBinary(column, position), zoneId);
+            case BLOB ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castBlob(
+                        columnBuilder, type.getBinary(column, position), zoneId);
+            case OBJECT ->
+                (column, columnBuilder, position, castValueService, zoneId) ->
+                    castValueService.castObject(columnBuilder, type.getBinary(column, position));
+            case ROW, UNKNOWN, VECTOR ->
+                (column, columnBuilder, position, castValueService, zoneId) -> {
+                  throw unsupportedCastSource(type);
+                };
+          };
+
+  /** Writes values produced by a cast into a column using the target type's representation. */
+  public static final TypeService<CastValueService> CAST_VALUE_SERVICE =
+      type ->
+          new CastValueService() {
+            @Override
+            public void castInt(ColumnBuilder columnBuilder, int value) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE -> type.writeInt(columnBuilder, value);
+                case INT64, TIMESTAMP -> type.writeLong(columnBuilder, value);
+                case FLOAT -> type.writeFloat(columnBuilder, value);
+                case DOUBLE -> type.writeDouble(columnBuilder, value);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0);
+                case TEXT, STRING ->
+                    type.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.intToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castDate(ColumnBuilder columnBuilder, int value, ZoneId zoneId) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE -> type.writeInt(columnBuilder, value);
+                case INT64 -> type.writeLong(columnBuilder, value);
+                case TIMESTAMP ->
+                    type.writeLong(
+                        columnBuilder,
+                        DateTimeUtils.correctPrecision(
+                            DateUtils.parseIntToTimestamp(value, zoneId)));
+                case FLOAT -> type.writeFloat(columnBuilder, value);
+                case DOUBLE -> type.writeDouble(columnBuilder, value);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0);
+                case TEXT, STRING ->
+                    type.writeBinary(
+                        columnBuilder, BytesUtils.valueOf(DateUtils.formatDate(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.intToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castTimestamp(ColumnBuilder columnBuilder, long value, ZoneId zoneId) {
+              try {
+                switch (type.getTypeEnum()) {
+                  case INT32 ->
+                      type.writeInt(columnBuilder, CastFunctionUtils.castLongToInt(value));
+                  case DATE ->
+                      type.writeInt(
+                          columnBuilder,
+                          DateUtils.parseDateExpressionToInt(
+                              DateTimeUtils.convertToLocalDate(value, zoneId)));
+                  case INT64, TIMESTAMP -> type.writeLong(columnBuilder, value);
+                  case FLOAT -> type.writeFloat(columnBuilder, value);
+                  case DOUBLE -> type.writeDouble(columnBuilder, value);
+                  case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0L);
+                  case TEXT, STRING ->
+                      type.writeBinary(
+                          columnBuilder,
+                          BytesUtils.valueOf(DateTimeUtils.convertLongToDate(value, zoneId)));
+                  case BLOB ->
+                      type.writeBinary(columnBuilder, new Binary(BytesUtils.longToBytes(value)));
+                  case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+                }
+              } catch (DateTimeParseException e) {
+                throw new IoTDBRuntimeException(
+                    "Year must be between 1000 and 9999.",
+                    org.apache.iotdb.rpc.TSStatusCode.DATE_OUT_OF_RANGE.getStatusCode(),
+                    true);
+              }
+            }
+
+            @Override
+            public void castLong(ColumnBuilder columnBuilder, long value) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    type.writeInt(columnBuilder, CastFunctionUtils.castLongToInt(value));
+                case INT64, TIMESTAMP -> type.writeLong(columnBuilder, value);
+                case FLOAT -> type.writeFloat(columnBuilder, value);
+                case DOUBLE -> type.writeDouble(columnBuilder, value);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0L);
+                case TEXT, STRING ->
+                    type.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.longToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castFloat(ColumnBuilder columnBuilder, float value) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    type.writeInt(columnBuilder, CastFunctionUtils.castFloatToInt(value));
+                case INT64, TIMESTAMP ->
+                    type.writeLong(columnBuilder, CastFunctionUtils.castFloatToLong(value));
+                case FLOAT -> type.writeFloat(columnBuilder, value);
+                case DOUBLE -> type.writeDouble(columnBuilder, value);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0.0f);
+                case TEXT, STRING ->
+                    type.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.floatToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castDouble(ColumnBuilder columnBuilder, double value) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    type.writeInt(columnBuilder, CastFunctionUtils.castDoubleToInt(value));
+                case INT64, TIMESTAMP ->
+                    type.writeLong(columnBuilder, CastFunctionUtils.castDoubleToLong(value));
+                case FLOAT ->
+                    type.writeFloat(columnBuilder, CastFunctionUtils.castDoubleToFloat(value));
+                case DOUBLE -> type.writeDouble(columnBuilder, value);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value != 0.0);
+                case TEXT, STRING ->
+                    type.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.doubleToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castBoolean(ColumnBuilder columnBuilder, boolean value) {
+              switch (type.getTypeEnum()) {
+                case INT32, DATE -> type.writeInt(columnBuilder, value ? 1 : 0);
+                case INT64, TIMESTAMP -> type.writeLong(columnBuilder, value ? 1L : 0L);
+                case FLOAT -> type.writeFloat(columnBuilder, value ? 1.0f : 0);
+                case DOUBLE -> type.writeDouble(columnBuilder, value ? 1.0 : 0);
+                case BOOLEAN -> type.writeBoolean(columnBuilder, value);
+                case TEXT, STRING ->
+                    type.writeBinary(columnBuilder, BytesUtils.valueOf(String.valueOf(value)));
+                case BLOB ->
+                    type.writeBinary(columnBuilder, new Binary(BytesUtils.boolToBytes(value)));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              }
+            }
+
+            @Override
+            public void castString(ColumnBuilder columnBuilder, Binary value, ZoneId zoneId) {
+              String stringValue = value.getStringValue(TSFileConfig.STRING_CHARSET);
+              try {
+                switch (type.getTypeEnum()) {
+                  case INT32 -> type.writeInt(columnBuilder, Integer.parseInt(stringValue));
+                  case DATE ->
+                      type.writeInt(columnBuilder, DateUtils.parseDateExpressionToInt(stringValue));
+                  case INT64 -> type.writeLong(columnBuilder, Long.parseLong(stringValue));
+                  case TIMESTAMP ->
+                      type.writeLong(
+                          columnBuilder,
+                          DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId));
+                  case FLOAT ->
+                      type.writeFloat(
+                          columnBuilder, CastFunctionUtils.castTextToFloat(stringValue));
+                  case DOUBLE ->
+                      type.writeDouble(
+                          columnBuilder, CastFunctionUtils.castTextToDouble(stringValue));
+                  case BOOLEAN ->
+                      type.writeBoolean(
+                          columnBuilder, CastFunctionUtils.castTextToBoolean(stringValue));
+                  case TEXT, STRING, BLOB, OBJECT -> type.writeBinary(columnBuilder, value);
+                  case ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+                }
+              } catch (DateTimeParseException | NumberFormatException e) {
+                throw new SemanticException(
+                    String.format("Cannot cast %s to %s type", stringValue, type.getDisplayName()));
+              }
+            }
+
+            @Override
+            public void castBlob(ColumnBuilder columnBuilder, Binary value, ZoneId zoneId) {
+              String stringValue = BytesUtils.parseBlobByteArrayToString(value.getValues());
+              try {
+                switch (type.getTypeEnum()) {
+                  case INT32 -> type.writeInt(columnBuilder, Integer.parseInt(stringValue));
+                  case DATE ->
+                      type.writeInt(columnBuilder, DateUtils.parseDateExpressionToInt(stringValue));
+                  case INT64 -> type.writeLong(columnBuilder, Long.parseLong(stringValue));
+                  case TIMESTAMP ->
+                      type.writeLong(
+                          columnBuilder,
+                          DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId));
+                  case FLOAT ->
+                      type.writeFloat(
+                          columnBuilder, CastFunctionUtils.castTextToFloat(stringValue));
+                  case DOUBLE ->
+                      type.writeDouble(
+                          columnBuilder, CastFunctionUtils.castTextToDouble(stringValue));
+                  case BOOLEAN ->
+                      type.writeBoolean(
+                          columnBuilder, CastFunctionUtils.castTextToBoolean(stringValue));
+                  case TEXT, STRING, BLOB -> type.writeBinary(columnBuilder, value);
+                  case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+                }
+              } catch (DateTimeParseException | NumberFormatException e) {
+                throw new SemanticException(
+                    String.format("Cannot cast %s to %s type", stringValue, type.getDisplayName()));
+              }
+            }
+
+            @Override
+            public void castObject(ColumnBuilder columnBuilder, Binary value) {
+              String stringValue = BytesUtils.parseObjectByteArrayToString(value.getValues());
+              switch (type.getTypeEnum()) {
+                case STRING ->
+                    type.writeBinary(
+                        columnBuilder, BytesUtils.valueOf(String.valueOf(stringValue)));
+                case INT32,
+                    INT64,
+                    FLOAT,
+                    DOUBLE,
+                    BOOLEAN,
+                    DATE,
+                    TIMESTAMP,
+                    TEXT,
+                    BLOB,
+                    OBJECT,
+                    ROW,
+                    UNKNOWN,
+                    VECTOR ->
+                    throw unsupportedCastTarget(type);
+              }
+            }
+          };
+
+  public static final TypeService<CastObjectValueService> CAST_OBJECT_VALUE_SERVICE =
+      type ->
+          new CastObjectValueService() {
+            @Override
+            public Object castInt(int value) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> value;
+                case INT64, TIMESTAMP -> (long) value;
+                case FLOAT -> (float) value;
+                case DOUBLE -> (double) value;
+                case BOOLEAN -> value != 0;
+                case TEXT, STRING -> BytesUtils.valueOf(String.valueOf(value));
+                case BLOB -> new Binary(BytesUtils.intToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castDate(int value, ZoneId zoneId) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> value;
+                case INT64 -> (long) value;
+                case TIMESTAMP ->
+                    DateTimeUtils.correctPrecision(DateUtils.parseIntToTimestamp(value, zoneId));
+                case FLOAT -> (float) value;
+                case DOUBLE -> (double) value;
+                case BOOLEAN -> value != 0;
+                case TEXT, STRING -> BytesUtils.valueOf(DateUtils.formatDate(value));
+                case BLOB -> new Binary(BytesUtils.intToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castTimestamp(long value, ZoneId zoneId) {
+              return switch (type.getTypeEnum()) {
+                case INT32 -> CastFunctionUtils.castLongToInt(value);
+                case DATE ->
+                    DateUtils.parseDateExpressionToInt(
+                        DateTimeUtils.convertToLocalDate(value, zoneId));
+                case INT64, TIMESTAMP -> value;
+                case FLOAT -> (float) value;
+                case DOUBLE -> (double) value;
+                case BOOLEAN -> value != 0L;
+                case TEXT, STRING ->
+                    BytesUtils.valueOf(DateTimeUtils.convertLongToDate(value, zoneId));
+                case BLOB -> new Binary(BytesUtils.longToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castLong(long value) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> CastFunctionUtils.castLongToInt(value);
+                case INT64, TIMESTAMP -> value;
+                case FLOAT -> (float) value;
+                case DOUBLE -> (double) value;
+                case BOOLEAN -> value != 0L;
+                case TEXT, STRING -> BytesUtils.valueOf(String.valueOf(value));
+                case BLOB -> new Binary(BytesUtils.longToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castFloat(float value) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> CastFunctionUtils.castFloatToInt(value);
+                case INT64, TIMESTAMP -> CastFunctionUtils.castFloatToLong(value);
+                case FLOAT -> value;
+                case DOUBLE -> (double) value;
+                case BOOLEAN -> value != 0.0f;
+                case TEXT, STRING -> BytesUtils.valueOf(String.valueOf(value));
+                case BLOB -> new Binary(BytesUtils.floatToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castDouble(double value) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> CastFunctionUtils.castDoubleToInt(value);
+                case INT64, TIMESTAMP -> CastFunctionUtils.castDoubleToLong(value);
+                case FLOAT -> CastFunctionUtils.castDoubleToFloat(value);
+                case DOUBLE -> value;
+                case BOOLEAN -> value != 0.0d;
+                case TEXT, STRING -> BytesUtils.valueOf(String.valueOf(value));
+                case BLOB -> new Binary(BytesUtils.doubleToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castBoolean(boolean value) {
+              return switch (type.getTypeEnum()) {
+                case INT32, DATE -> value ? 1 : 0;
+                case INT64, TIMESTAMP -> value ? 1L : 0L;
+                case FLOAT -> value ? 1.0f : 0.0f;
+                case DOUBLE -> value ? 1.0d : 0.0d;
+                case BOOLEAN -> value;
+                case TEXT, STRING -> BytesUtils.valueOf(String.valueOf(value));
+                case BLOB -> new Binary(BytesUtils.boolToBytes(value));
+                case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+              };
+            }
+
+            @Override
+            public Object castBinary(Binary value, ZoneId zoneId) {
+              String stringValue = value.getStringValue(TSFileConfig.STRING_CHARSET);
+              try {
+                return switch (type.getTypeEnum()) {
+                  case INT32 -> Integer.parseInt(stringValue);
+                  case DATE -> DateUtils.parseDateExpressionToInt(stringValue);
+                  case INT64 -> Long.parseLong(stringValue);
+                  case TIMESTAMP -> DateTimeUtils.convertDatetimeStrToLong(stringValue, zoneId);
+                  case FLOAT -> CastFunctionUtils.castTextToFloat(stringValue);
+                  case DOUBLE -> CastFunctionUtils.castTextToDouble(stringValue);
+                  case BOOLEAN -> CastFunctionUtils.castTextToBoolean(stringValue);
+                  case TEXT, STRING, BLOB -> value;
+                  case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedCastTarget(type);
+                };
+              } catch (DateTimeParseException | NumberFormatException e) {
+                throw new SemanticException(
+                    String.format("Cannot cast %s to %s type", stringValue, type.getDisplayName()));
+              }
+            }
+          };
+
+  public static final TypeService<CastObjectInputService> CAST_OBJECT_INPUT_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (value, castValueService, zoneId) ->
+                    castValueService.castInt(
+                        value instanceof Integer ? (int) value : ((Long) value).intValue());
+            case DATE ->
+                (value, castValueService, zoneId) ->
+                    castValueService.castDate(
+                        value instanceof Integer ? (int) value : ((Long) value).intValue(), zoneId);
+            case INT64 ->
+                (value, castValueService, zoneId) -> castValueService.castLong((Long) value);
+            case TIMESTAMP ->
+                (value, castValueService, zoneId) ->
+                    castValueService.castTimestamp((Long) value, zoneId);
+            case FLOAT ->
+                (value, castValueService, zoneId) ->
+                    castValueService.castFloat(
+                        value instanceof Float ? (float) value : ((Double) value).floatValue());
+            case DOUBLE ->
+                (value, castValueService, zoneId) -> castValueService.castDouble((Double) value);
+            case BOOLEAN ->
+                (value, castValueService, zoneId) -> castValueService.castBoolean((Boolean) value);
+            case TEXT, STRING, BLOB ->
+                (value, castValueService, zoneId) ->
+                    castValueService.castBinary((Binary) value, zoneId);
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (value, castValueService, zoneId) -> {
+                  throw unsupportedCastSource(type);
+                };
+          };
+
+  public static final TypeService<FormatValueConverter> FORMAT_VALUE_CONVERTER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case UNKNOWN -> (column, position, zoneId) -> null;
+            case INT32, INT64, FLOAT, DOUBLE, BOOLEAN, TEXT, STRING, BLOB ->
+                (column, position, zoneId) -> column.getObject(position);
+            // Java Formatter expects temporal objects for DATE and TIMESTAMP arguments.
+            case DATE ->
+                (column, position, zoneId) ->
+                    DateUtils.parseIntToLocalDate(column.getInt(position));
+            case TIMESTAMP ->
+                (column, position, zoneId) ->
+                    DateTimeUtils.convertToZonedDateTime(column.getLong(position), zoneId);
+            case OBJECT, ROW, VECTOR ->
+                (column, position, zoneId) -> {
+                  throw new UnsupportedOperationException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
+                };
+          };
+
+  public static final TypeService<GroupedAccumulator>
+      GROUPED_APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case BOOLEAN -> new BooleanGroupedApproxMostFrequentAccumulator();
+                case INT32, DATE -> new IntGroupedApproxMostFrequentAccumulator();
+                case INT64, TIMESTAMP -> new LongGroupedApproxMostFrequentAccumulator();
+                case FLOAT -> new FloatGroupedApproxMostFrequentAccumulator();
+                case DOUBLE -> new DoubleGroupedApproxMostFrequentAccumulator();
+                case TEXT, STRING -> new BinaryGroupedApproxMostFrequentAccumulator();
+                case BLOB -> new BlobGroupedApproxMostFrequentAccumulator();
+                case OBJECT, ROW, UNKNOWN, VECTOR ->
+                    throw unsupportedApproxMostFrequentDataType(type);
+              };
+
+  public static final TypeService<TableAccumulator> APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> new BooleanApproxMostFrequentAccumulator();
+            case INT32, DATE -> new IntApproxMostFrequentAccumulator();
+            case INT64, TIMESTAMP -> new LongApproxMostFrequentAccumulator();
+            case FLOAT -> new FloatApproxMostFrequentAccumulator();
+            case DOUBLE -> new DoubleApproxMostFrequentAccumulator();
+            case TEXT, STRING -> new BinaryApproxMostFrequentAccumulator();
+            case BLOB -> new BlobApproxMostFrequentAccumulator();
+            case OBJECT, ROW, UNKNOWN, VECTOR -> throw unsupportedApproxMostFrequentDataType(type);
+          };
+
   public static final TypeService<Function<List<ColumnTransformer>, ColumnTransformer>>
       GREATEST_COLUMN_TRANSFORMER_SERVICE =
           type ->
@@ -357,31 +957,1259 @@ public class TypeServices {
             return columnBuilder.build();
           };
 
-  /** Copies a column value into a reusable primitive result without boxing. */
-  public static final TypeService<PrimitiveColumnValueSetter>
-      PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE =
+  /** Copies a column value into a reusable primitive result without boxing. */
+  public static final TypeService<PrimitiveColumnValueSetter>
+      PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    (target, column, position) -> target.setInt(type.getInt(column, position));
+                case INT64, TIMESTAMP ->
+                    (target, column, position) -> target.setLong(type.getLong(column, position));
+                case FLOAT ->
+                    (target, column, position) -> target.setFloat(type.getFloat(column, position));
+                case DOUBLE ->
+                    (target, column, position) ->
+                        target.setDouble(type.getDouble(column, position));
+                case BOOLEAN ->
+                    (target, column, position) ->
+                        target.setBoolean(type.getBoolean(column, position));
+                case TEXT, STRING, BLOB, OBJECT ->
+                    (target, column, position) ->
+                        target.setBinary(type.getBinary(column, position));
+                case ROW, UNKNOWN, VECTOR ->
+                    (target, column, position) -> {
+                      throw new UnSupportedDataTypeException(
+                              CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum())
+                          .setChecked(true);
+                    };
+              };
+
+  /** Writes a statistics value into a reusable primitive result. */
+  public static final TypeService<StatisticsValueSetter> STATISTICS_VALUE_SETTER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (target, value, exceptionSupplier) -> target.setInt(((Number) value).intValue());
+            case INT64, TIMESTAMP ->
+                (target, value, exceptionSupplier) -> target.setLong(((Number) value).longValue());
+            case FLOAT ->
+                (target, value, exceptionSupplier) ->
+                    target.setFloat(((Number) value).floatValue());
+            case DOUBLE ->
+                (target, value, exceptionSupplier) ->
+                    target.setDouble(((Number) value).doubleValue());
+            case BOOLEAN ->
+                (target, value, exceptionSupplier) -> target.setBoolean((boolean) value);
+            case TEXT, STRING, BLOB, OBJECT ->
+                (target, value, exceptionSupplier) -> target.setBinary(toBinary(value));
+            case ROW, UNKNOWN, VECTOR ->
+                (target, value, exceptionSupplier) -> {
+                  throw exceptionSupplier.get();
+                };
+          };
+
+  public static final TypeService<InColumnValueMatcherFactory> IN_COLUMN_VALUE_MATCHER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                values -> {
+                  Set<Integer> intSet = new HashSet<>();
+                  for (String value : values) {
+                    try {
+                      intSet.add(Integer.valueOf(value));
+                    } catch (IllegalArgumentException e) {
+                      throw new SemanticException(
+                          String.format(
+                              CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                    }
+                  }
+                  return (column, position) -> intSet.contains(type.getInt(column, position));
+                };
+            case INT64, TIMESTAMP ->
+                values -> {
+                  Set<Long> longSet = new HashSet<>();
+                  for (String value : values) {
+                    try {
+                      longSet.add(Long.valueOf(value));
+                    } catch (IllegalArgumentException e) {
+                      throw new SemanticException(
+                          String.format(
+                              CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                    }
+                  }
+                  return (column, position) -> longSet.contains(type.getLong(column, position));
+                };
+            case FLOAT ->
+                values -> {
+                  Set<Float> floatSet = new HashSet<>();
+                  for (String value : values) {
+                    try {
+                      floatSet.add(Float.valueOf(value));
+                    } catch (IllegalArgumentException e) {
+                      throw new SemanticException(
+                          String.format(
+                              CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                    }
+                  }
+                  return (column, position) -> floatSet.contains(type.getFloat(column, position));
+                };
+            case DOUBLE ->
+                values -> {
+                  Set<Double> doubleSet = new HashSet<>();
+                  for (String value : values) {
+                    try {
+                      doubleSet.add(Double.valueOf(value));
+                    } catch (IllegalArgumentException e) {
+                      throw new SemanticException(
+                          String.format(
+                              CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                    }
+                  }
+                  return (column, position) -> doubleSet.contains(type.getDouble(column, position));
+                };
+            case BOOLEAN ->
+                values -> {
+                  Set<Boolean> booleanSet = new HashSet<>();
+                  for (String value : values) {
+                    if ("true".equalsIgnoreCase(value)) {
+                      booleanSet.add(true);
+                    } else if ("false".equalsIgnoreCase(value)) {
+                      booleanSet.add(false);
+                    } else {
+                      throw new SemanticException(
+                          String.format(CalcMessages.CANNOT_CAST_TO_BOOLEAN, value));
+                    }
+                  }
+                  return (column, position) ->
+                      booleanSet.contains(type.getBoolean(column, position));
+                };
+            case TEXT, STRING ->
+                values -> {
+                  Set<Binary> stringSet =
+                      values.stream()
+                          .map(value -> new Binary(value, TSFileConfig.STRING_CHARSET))
+                          .collect(Collectors.toSet());
+                  return (column, position) -> stringSet.contains(type.getBinary(column, position));
+                };
+            case BLOB, DATE, OBJECT, ROW, UNKNOWN, VECTOR ->
+                values -> {
+                  throw new UnsupportedOperationException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE_LOWER + type.getTypeEnum());
+                };
+          };
+
+  public static final TypeService<InMultiColumnTransformerFactory>
+      IN_MULTI_COLUMN_TRANSFORMER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32 ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Integer> intSet = new HashSet<>();
+                      for (Literal value : values) {
+                        try {
+                          long v = ((LongLiteral) value).getParsedValue();
+                          if (v <= Integer.MAX_VALUE && v >= Integer.MIN_VALUE) {
+                            intSet.add((int) v);
+                          }
+                        } catch (IllegalArgumentException e) {
+                          throw new SemanticException(
+                              String.format(
+                                  CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                        }
+                      }
+                      return new InInt32MultiColumnTransformer(intSet, valueColumnTransformers);
+                    };
+                case DATE ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Integer> dateSet = new HashSet<>();
+                      for (Literal value : values) {
+                        dateSet.add(Integer.parseInt(((GenericLiteral) value).getValue()));
+                      }
+                      return new InInt32MultiColumnTransformer(dateSet, valueColumnTransformers);
+                    };
+                case INT64 ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Long> longSet = new HashSet<>();
+                      for (Literal value : values) {
+                        longSet.add(((LongLiteral) value).getParsedValue());
+                      }
+                      return new InInt64MultiColumnTransformer(longSet, valueColumnTransformers);
+                    };
+                case TIMESTAMP ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Long> timestampSet = new HashSet<>();
+                      for (Literal value : values) {
+                        try {
+                          if (value instanceof LongLiteral) {
+                            timestampSet.add(((LongLiteral) value).getParsedValue());
+                          } else if (value instanceof DoubleLiteral) {
+                            timestampSet.add((long) ((DoubleLiteral) value).getValue());
+                          } else if (value instanceof FloatLiteral) {
+                            timestampSet.add((long) ((FloatLiteral) value).getValue());
+                          } else if (value instanceof GenericLiteral) {
+                            timestampSet.add(Long.parseLong(((GenericLiteral) value).getValue()));
+                          } else {
+                            throw new SemanticException(
+                                String.format(
+                                    CalcMessages.IN_LIST_LITERAL_FOR_TIMESTAMP_TYPE_RESTRICTION,
+                                    value.getClass().getSimpleName()));
+                          }
+                        } catch (IllegalArgumentException e) {
+                          throw new SemanticException(
+                              String.format(
+                                  CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                        }
+                      }
+                      return new InInt64MultiColumnTransformer(
+                          timestampSet, valueColumnTransformers);
+                    };
+                case FLOAT ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Float> floatSet = new HashSet<>();
+                      for (Literal value : values) {
+                        try {
+                          if (value instanceof FloatLiteral) {
+                            floatSet.add(((FloatLiteral) value).getValue());
+                          } else {
+                            floatSet.add((float) ((DoubleLiteral) value).getValue());
+                          }
+                        } catch (IllegalArgumentException e) {
+                          throw new SemanticException(
+                              String.format(
+                                  CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                        }
+                      }
+                      return new InFloatMultiColumnTransformer(floatSet, valueColumnTransformers);
+                    };
+                case DOUBLE ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Double> doubleSet = new HashSet<>();
+                      for (Literal value : values) {
+                        try {
+                          if (value instanceof FloatLiteral) {
+                            doubleSet.add((double) ((FloatLiteral) value).getValue());
+                          } else {
+                            doubleSet.add(((DoubleLiteral) value).getValue());
+                          }
+                        } catch (IllegalArgumentException e) {
+                          throw new SemanticException(
+                              String.format(
+                                  CalcMessages.CANNOT_CAST_TO_TYPE, value, type.getTypeEnum()));
+                        }
+                      }
+                      return new InDoubleMultiColumnTransformer(doubleSet, valueColumnTransformers);
+                    };
+                case BOOLEAN ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Boolean> booleanSet = new HashSet<>();
+                      for (Literal value : values) {
+                        booleanSet.add(((BooleanLiteral) value).getValue());
+                      }
+                      return new InBooleanMultiColumnTransformer(
+                          booleanSet, valueColumnTransformers);
+                    };
+                case TEXT, STRING ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Binary> stringSet = new HashSet<>();
+                      for (Literal value : values) {
+                        stringSet.add(
+                            new Binary(
+                                ((StringLiteral) value).getValue(), TSFileConfig.STRING_CHARSET));
+                      }
+                      return new InBinaryMultiColumnTransformer(stringSet, valueColumnTransformers);
+                    };
+                case BLOB ->
+                    (valueColumnTransformers, values) -> {
+                      Set<Binary> binarySet = new HashSet<>();
+                      for (Literal value : values) {
+                        binarySet.add(new Binary(((BinaryLiteral) value).getValue()));
+                      }
+                      return new InBinaryMultiColumnTransformer(binarySet, valueColumnTransformers);
+                    };
+                case OBJECT, ROW, UNKNOWN, VECTOR ->
+                    (valueColumnTransformers, values) -> {
+                      throw new UnsupportedOperationException(
+                          CalcMessages.UNSUPPORTED_DATA_TYPE_LOWER + type.getTypeEnum());
+                    };
+              };
+
+  /** Updates a result with the minimum value from statistics. */
+  public static final TypeService<StatisticsValueUpdater> MIN_STATISTICS_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  int candidate = ((Number) value).intValue();
+                  if (!initialized || candidate < result.getInt()) {
+                    result.setInt(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64, TIMESTAMP ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  long candidate = ((Number) value).longValue();
+                  if (!initialized || candidate < result.getLong()) {
+                    result.setLong(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  float candidate = ((Number) value).floatValue();
+                  if (!initialized || candidate < result.getFloat()) {
+                    result.setFloat(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  double candidate = ((Number) value).doubleValue();
+                  if (!initialized || candidate < result.getDouble()) {
+                    result.setDouble(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  boolean candidate = (boolean) value;
+                  if (!initialized || !candidate) {
+                    result.setBoolean(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case TEXT, STRING, BLOB ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  Binary candidate = toBinary(value);
+                  if (!initialized || candidate.compareTo(result.getBinary()) < 0) {
+                    result.setBinary(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  throw exceptionSupplier.get();
+                };
+          };
+
+  /** Updates a result with the maximum value from statistics. */
+  public static final TypeService<StatisticsValueUpdater> MAX_STATISTICS_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  int candidate = ((Number) value).intValue();
+                  if (!initialized || candidate > result.getInt()) {
+                    result.setInt(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64, TIMESTAMP ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  long candidate = ((Number) value).longValue();
+                  if (!initialized || candidate > result.getLong()) {
+                    result.setLong(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  float candidate = ((Number) value).floatValue();
+                  if (!initialized || candidate > result.getFloat()) {
+                    result.setFloat(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  double candidate = ((Number) value).doubleValue();
+                  if (!initialized || candidate > result.getDouble()) {
+                    result.setDouble(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  boolean candidate = (boolean) value;
+                  if (!initialized || candidate) {
+                    result.setBoolean(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case TEXT, STRING, BLOB ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  Binary candidate = toBinary(value);
+                  if (!initialized || candidate.compareTo(result.getBinary()) > 0) {
+                    result.setBinary(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  throw exceptionSupplier.get();
+                };
+          };
+
+  /** Updates a result with the greatest absolute numeric value from statistics. */
+  public static final TypeService<StatisticsValueUpdater> EXTREME_STATISTICS_VALUE_UPDATER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  int candidate = ((Number) value).intValue();
+                  if (!initialized || compareExtreme(candidate, result.getInt()) > 0) {
+                    result.setInt(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case INT64 ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  long candidate = ((Number) value).longValue();
+                  if (!initialized || compareExtreme(candidate, result.getLong()) > 0) {
+                    result.setLong(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case FLOAT ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  float candidate = ((Number) value).floatValue();
+                  float absCandidate = Math.abs(candidate);
+                  float absResult = Math.abs(result.getFloat());
+                  if (!initialized
+                      || absCandidate > absResult
+                      || absCandidate == absResult && candidate > result.getFloat()) {
+                    result.setFloat(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case DOUBLE ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  double candidate = ((Number) value).doubleValue();
+                  double absCandidate = Math.abs(candidate);
+                  double absResult = Math.abs(result.getDouble());
+                  if (!initialized
+                      || absCandidate > absResult
+                      || absCandidate == absResult && candidate > result.getDouble()) {
+                    result.setDouble(candidate);
+                    return true;
+                  }
+                  return false;
+                };
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (result, value, initialized, exceptionSupplier) -> {
+                  throw exceptionSupplier.get();
+                };
+          };
+
+  /** Binds an APPROX_PERCENTILE input reader to the input column's native type. */
+  public static final TypeService<
+          Function<AbstractApproxPercentileAccumulator, BiConsumer<Column[], AggregationMask>>>
+      APPROX_PERCENTILE_INPUT_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32 -> accumulator -> accumulator::addIntInput;
+                case INT64, TIMESTAMP -> accumulator -> accumulator::addLongInput;
+                case FLOAT -> accumulator -> accumulator::addFloatInput;
+                case DOUBLE -> accumulator -> accumulator::addDoubleInput;
+                case BOOLEAN, DATE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    accumulator ->
+                        (arguments, mask) -> {
+                          throw new UnSupportedDataTypeException(
+                              String.format(
+                                  CalcMessages
+                                      .UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
+                                  type.getTypeEnum()));
+                        };
+              };
+
+  /** Binds a grouped APPROX_PERCENTILE input reader to the input column's native type. */
+  public static final TypeService<
+          Function<
+              AbstractGroupedApproxPercentileAccumulator,
+              TriConsumer<int[], Column[], AggregationMask>>>
+      GROUPED_APPROX_PERCENTILE_INPUT_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32 -> accumulator -> accumulator::addIntInput;
+                case INT64, TIMESTAMP -> accumulator -> accumulator::addLongInput;
+                case FLOAT -> accumulator -> accumulator::addFloatInput;
+                case DOUBLE -> accumulator -> accumulator::addDoubleInput;
+                case BOOLEAN, DATE, TEXT, BLOB, STRING, OBJECT, ROW, UNKNOWN, VECTOR ->
+                    accumulator ->
+                        (groupIds, arguments, mask) -> {
+                          throw new UnSupportedDataTypeException(
+                              String.format(
+                                  CalcMessages
+                                      .UNSUPPORTED_DATA_TYPE_IN_APPROX_PERCENTILE_AGGREGATION,
+                                  type.getTypeEnum()));
+                        };
+              };
+
+  /** Provides storage operations for grouped accumulators backed by primitive big arrays. */
+  public static final TypeService<GroupedValueService> GROUPED_VALUE_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeIntValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getIntValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getIntValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getIntValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeInt(accessor.getIntValues().get(groupId));
+                  }
+                };
+            case INT64, TIMESTAMP ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeLongValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getLongValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getLongValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getLongValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeLong(accessor.getLongValues().get(groupId));
+                  }
+                };
+            case FLOAT ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeFloatValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getFloatValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getFloatValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getFloatValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeFloat(accessor.getFloatValues().get(groupId));
+                  }
+                };
+            case DOUBLE ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeDoubleValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getDoubleValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getDoubleValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getDoubleValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeDouble(accessor.getDoubleValues().get(groupId));
+                  }
+                };
+            case TEXT, STRING, BLOB, OBJECT ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeBinaryValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getBinaryValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getBinaryValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getBinaryValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeBinary(accessor.getBinaryValues().get(groupId));
+                  }
+                };
+            case BOOLEAN ->
+                new GroupedValueService() {
+                  @Override
+                  public void initialize(GroupedValueAccessor accessor) {
+                    accessor.initializeBooleanValues();
+                  }
+
+                  @Override
+                  public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+                    accessor.getBooleanValues().ensureCapacity(groupCount);
+                  }
+
+                  @Override
+                  public long sizeOf(GroupedValueAccessor accessor) {
+                    return accessor.getBooleanValues().sizeOf();
+                  }
+
+                  @Override
+                  public void reset(GroupedValueAccessor accessor) {
+                    accessor.getBooleanValues().reset();
+                  }
+
+                  @Override
+                  public void write(
+                      GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+                    columnBuilder.writeBoolean(accessor.getBooleanValues().get(groupId));
+                  }
+                };
+            case ROW, UNKNOWN, VECTOR -> unsupportedGroupedValueService(type);
+          };
+
+  /** Reads grouped input and intermediate columns using the native column accessor. */
+  public static final TypeService<GroupedInputReader> GROUPED_INPUT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateInt(groupIds[position], arguments[0].getInt(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateInt(groupIds[i], argument.getInt(i));
+                      }
+                    }
+                  }
+                };
+            case INT64, TIMESTAMP ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateLong(
+                                groupIds[position], arguments[0].getLong(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateLong(groupIds[i], argument.getLong(i));
+                      }
+                    }
+                  }
+                };
+            case FLOAT ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateFloat(
+                                groupIds[position], arguments[0].getFloat(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateFloat(groupIds[i], argument.getFloat(i));
+                      }
+                    }
+                  }
+                };
+            case DOUBLE ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateDouble(
+                                groupIds[position], arguments[0].getDouble(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateDouble(groupIds[i], argument.getDouble(i));
+                      }
+                    }
+                  }
+                };
+            case TEXT, STRING, BLOB, OBJECT ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateBinary(
+                                groupIds[position], arguments[0].getBinary(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateBinary(groupIds[i], argument.getBinary(i));
+                      }
+                    }
+                  }
+                };
+            case BOOLEAN ->
+                new GroupedInputReader() {
+                  @Override
+                  public void addInput(
+                      int[] groupIds,
+                      Column[] arguments,
+                      AggregationMask mask,
+                      GroupedInputConsumer consumer) {
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateBoolean(
+                                groupIds[position], arguments[0].getBoolean(position)));
+                  }
+
+                  @Override
+                  public void addIntermediate(
+                      int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+                    for (int i = 0; i < groupIds.length; i++) {
+                      if (!argument.isNull(i)) {
+                        consumer.updateBoolean(groupIds[i], argument.getBoolean(i));
+                      }
+                    }
+                  }
+                };
+            case ROW, UNKNOWN, VECTOR -> unsupportedGroupedInputReader(type);
+          };
+
+  /** Restricts grouped EXTREME to the numeric types supported by the aggregate. */
+  public static final TypeService<GroupedValueService> GROUPED_EXTREME_VALUE_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, INT64, FLOAT, DOUBLE -> GROUPED_VALUE_SERVICE.call(type);
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                unsupportedGroupedValueService(type);
+          };
+
+  public static final TypeService<GroupedInputReader> GROUPED_EXTREME_INPUT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, INT64, FLOAT, DOUBLE -> GROUPED_INPUT_READER_SERVICE.call(type);
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                unsupportedGroupedInputReader(type);
+          };
+
+  /** Reads a value column together with its optional ordering-time column. */
+  public static final TypeService<GroupedTimeInputReader> GROUPED_TIME_INPUT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateInt(
+                                groupIds[position],
+                                arguments[0].getInt(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case INT64, TIMESTAMP ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateLong(
+                                groupIds[position],
+                                arguments[0].getLong(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case FLOAT ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateFloat(
+                                groupIds[position],
+                                arguments[0].getFloat(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case DOUBLE ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateDouble(
+                                groupIds[position],
+                                arguments[0].getDouble(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case TEXT, STRING, BLOB, OBJECT ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateBinary(
+                                groupIds[position],
+                                arguments[0].getBinary(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case BOOLEAN ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[0],
+                        mask,
+                        position ->
+                            consumer.updateBoolean(
+                                groupIds[position],
+                                arguments[0].getBoolean(position),
+                                arguments[1].isNull(position),
+                                arguments[1].isNull(position)
+                                    ? 0
+                                    : arguments[1].getLong(position)));
+            case ROW, UNKNOWN, VECTOR ->
+                (groupIds, arguments, mask, consumer) -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  /** Reads x values for FIRST_BY/LAST_BY while filtering null y values. */
+  public static final TypeService<GroupedByInputReader> GROUPED_BY_INPUT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateInt(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getInt(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case INT64, TIMESTAMP ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateLong(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getLong(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case FLOAT ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateFloat(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getFloat(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case DOUBLE ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateDouble(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getDouble(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case TEXT, STRING, BLOB, OBJECT ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateBinary(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getBinary(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case BOOLEAN ->
+                (groupIds, arguments, mask, consumer) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            consumer.updateBoolean(
+                                groupIds[position],
+                                arguments[0].isNull(position),
+                                arguments[0].getBoolean(position),
+                                arguments[2].isNull(position),
+                                arguments[2].isNull(position)
+                                    ? 0
+                                    : arguments[2].getLong(position)));
+            case ROW, UNKNOWN, VECTOR ->
+                (groupIds, arguments, mask, consumer) -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  /** Serializes/deserializes a grouped value in the native intermediate format. */
+  public static final TypeService<GroupedValueSerializer> GROUPED_VALUE_SERIALIZER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Integer.BYTES;
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    accessor.getIntValues().toBytes(index, bytes, offset);
+                  }
+                };
+            case INT64, TIMESTAMP ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Long.BYTES;
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    accessor.getLongValues().toBytes(index, bytes, offset);
+                  }
+                };
+            case FLOAT ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Float.BYTES;
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    accessor.getFloatValues().toBytes(index, bytes, offset);
+                  }
+                };
+            case DOUBLE ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Double.BYTES;
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    accessor.getDoubleValues().toBytes(index, bytes, offset);
+                  }
+                };
+            case TEXT, STRING, BLOB, OBJECT ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Integer.BYTES + accessor.getBinaryValues().get(index).getLength();
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    accessor.getBinaryValues().toBytes(index, bytes, offset);
+                  }
+                };
+            case BOOLEAN ->
+                new GroupedValueSerializer() {
+                  @Override
+                  public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+                    return Byte.BYTES;
+                  }
+
+                  @Override
+                  public void serialize(
+                      GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+                    BytesUtils.boolToBytes(accessor.getBooleanValues().get(index), bytes, offset);
+                  }
+                };
+            case ROW, UNKNOWN, VECTOR -> unsupportedGroupedValueSerializer(type);
+          };
+
+  /** Stores a column value in the typed big array selected for a grouped accumulator. */
+  public static final TypeService<GroupedValueSetter> GROUPED_VALUE_SETTER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (accessor, index, column, position) ->
+                    accessor.getIntValues().set(index, type.getInt(column, position));
+            case INT64, TIMESTAMP ->
+                (accessor, index, column, position) ->
+                    accessor.getLongValues().set(index, type.getLong(column, position));
+            case FLOAT ->
+                (accessor, index, column, position) ->
+                    accessor.getFloatValues().set(index, type.getFloat(column, position));
+            case DOUBLE ->
+                (accessor, index, column, position) ->
+                    accessor.getDoubleValues().set(index, type.getDouble(column, position));
+            case TEXT, STRING, BLOB, OBJECT ->
+                (accessor, index, column, position) ->
+                    accessor.getBinaryValues().set(index, type.getBinary(column, position));
+            case BOOLEAN ->
+                (accessor, index, column, position) ->
+                    accessor.getBooleanValues().set(index, type.getBoolean(column, position));
+            case ROW, UNKNOWN, VECTOR ->
+                (accessor, index, column, position) -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  /** Reads a grouped value from bytes into a typed callback. */
+  public static final TypeService<GroupedTimeValueDeserializer>
+      GROUPED_TIME_VALUE_DESERIALIZER_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    (bytes, offset, groupId, time, timeNull, consumer) ->
+                        consumer.updateInt(
+                            groupId, BytesUtils.bytesToInt(bytes, offset), timeNull, time);
+                case INT64, TIMESTAMP ->
+                    (bytes, offset, groupId, time, timeNull, consumer) ->
+                        consumer.updateLong(
+                            groupId,
+                            BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset),
+                            timeNull,
+                            time);
+                case FLOAT ->
+                    (bytes, offset, groupId, time, timeNull, consumer) ->
+                        consumer.updateFloat(
+                            groupId, BytesUtils.bytesToFloat(bytes, offset), timeNull, time);
+                case DOUBLE ->
+                    (bytes, offset, groupId, time, timeNull, consumer) ->
+                        consumer.updateDouble(
+                            groupId, BytesUtils.bytesToDouble(bytes, offset), timeNull, time);
+                case TEXT, STRING, BLOB, OBJECT ->
+                    (bytes, offset, groupId, time, timeNull, consumer) -> {
+                      int length = BytesUtils.bytesToInt(bytes, offset);
+                      consumer.updateBinary(
+                          groupId,
+                          new Binary(BytesUtils.subBytes(bytes, offset + Integer.BYTES, length)),
+                          timeNull,
+                          time);
+                    };
+                case BOOLEAN ->
+                    (bytes, offset, groupId, time, timeNull, consumer) ->
+                        consumer.updateBoolean(
+                            groupId, BytesUtils.bytesToBool(bytes, offset), timeNull, time);
+                case ROW, UNKNOWN, VECTOR ->
+                    (bytes, offset, groupId, time, timeNull, consumer) -> {
+                      throw unsupportedDataType(type);
+                    };
+              };
+
+  /** Reads x values from FIRST_BY/LAST_BY intermediate bytes. */
+  public static final TypeService<GroupedByValueDeserializer>
+      GROUPED_BY_VALUE_DESERIALIZER_SERVICE =
           type ->
               switch (type.getTypeEnum()) {
                 case INT32, DATE ->
-                    (target, column, position) -> target.setInt(type.getInt(column, position));
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) ->
+                        consumer.updateInt(
+                            groupId,
+                            xNull,
+                            xNull ? 0 : BytesUtils.bytesToInt(bytes, offset),
+                            timeNull,
+                            time);
                 case INT64, TIMESTAMP ->
-                    (target, column, position) -> target.setLong(type.getLong(column, position));
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) ->
+                        consumer.updateLong(
+                            groupId,
+                            xNull,
+                            xNull ? 0 : BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, offset),
+                            timeNull,
+                            time);
                 case FLOAT ->
-                    (target, column, position) -> target.setFloat(type.getFloat(column, position));
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) ->
+                        consumer.updateFloat(
+                            groupId,
+                            xNull,
+                            xNull ? 0 : BytesUtils.bytesToFloat(bytes, offset),
+                            timeNull,
+                            time);
                 case DOUBLE ->
-                    (target, column, position) ->
-                        target.setDouble(type.getDouble(column, position));
-                case BOOLEAN ->
-                    (target, column, position) ->
-                        target.setBoolean(type.getBoolean(column, position));
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) ->
+                        consumer.updateDouble(
+                            groupId,
+                            xNull,
+                            xNull ? 0 : BytesUtils.bytesToDouble(bytes, offset),
+                            timeNull,
+                            time);
                 case TEXT, STRING, BLOB, OBJECT ->
-                    (target, column, position) ->
-                        target.setBinary(type.getBinary(column, position));
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) -> {
+                      Binary value = null;
+                      if (!xNull) {
+                        int length = BytesUtils.bytesToInt(bytes, offset);
+                        value =
+                            new Binary(BytesUtils.subBytes(bytes, offset + Integer.BYTES, length));
+                      }
+                      consumer.updateBinary(groupId, xNull, value, timeNull, time);
+                    };
+                case BOOLEAN ->
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) ->
+                        consumer.updateBoolean(
+                            groupId,
+                            xNull,
+                            xNull ? false : BytesUtils.bytesToBool(bytes, offset),
+                            timeNull,
+                            time);
                 case ROW, UNKNOWN, VECTOR ->
-                    (target, column, position) -> {
-                      throw new UnSupportedDataTypeException(
-                              CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum())
-                          .setChecked(true);
+                    (bytes, offset, groupId, xNull, time, timeNull, consumer) -> {
+                      throw unsupportedDataType(type);
                     };
               };
 
@@ -491,6 +2319,131 @@ public class TypeServices {
                 };
           };
 
+  /** Selects the y-column reader used by grouped MAX_BY and MIN_BY input processing. */
+  public static final TypeService<GroupedMaxMinByInput> GROUPED_MAX_MIN_BY_INPUT_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateInt(
+                                groupIds[position],
+                                type.getInt(arguments[1], position),
+                                arguments[0],
+                                position));
+            case INT64, TIMESTAMP ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateLong(
+                                groupIds[position],
+                                type.getLong(arguments[1], position),
+                                arguments[0],
+                                position));
+            case FLOAT ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateFloat(
+                                groupIds[position],
+                                type.getFloat(arguments[1], position),
+                                arguments[0],
+                                position));
+            case DOUBLE ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateDouble(
+                                groupIds[position],
+                                type.getDouble(arguments[1], position),
+                                arguments[0],
+                                position));
+            case TEXT, STRING, BLOB, OBJECT ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateBinary(
+                                groupIds[position],
+                                type.getBinary(arguments[1], position),
+                                arguments[0],
+                                position));
+            case BOOLEAN ->
+                (groupIds, arguments, mask, updater) ->
+                    forEachSelectedPosition(
+                        arguments[1],
+                        mask,
+                        position ->
+                            updater.updateBoolean(
+                                groupIds[position],
+                                type.getBoolean(arguments[1], position),
+                                arguments[0],
+                                position));
+            case ROW, UNKNOWN, VECTOR ->
+                (groupIds, arguments, mask, updater) -> {
+                  throw unsupportedMaxMinByDataType(type);
+                };
+          };
+
+  /** Selects the y-value decoder used by grouped MAX_BY and MIN_BY intermediate input. */
+  public static final TypeService<GroupedMaxMinByIntermediateInput>
+      GROUPED_MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE =
+          type ->
+              switch (type.getTypeEnum()) {
+                case INT32, DATE ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      int value = BytesUtils.bytesToInt(bytes, 0);
+                      readMaxMinByXFromBytes(bytes, Integer.BYTES, xType, columnBuilder);
+                      updater.updateInt(groupId, value, columnBuilder.build(), 0);
+                    };
+                case INT64, TIMESTAMP ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      long value = BytesUtils.bytesToLongFromOffset(bytes, Long.BYTES, 0);
+                      readMaxMinByXFromBytes(bytes, Long.BYTES, xType, columnBuilder);
+                      updater.updateLong(groupId, value, columnBuilder.build(), 0);
+                    };
+                case FLOAT ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      float value = BytesUtils.bytesToFloat(bytes, 0);
+                      readMaxMinByXFromBytes(bytes, Float.BYTES, xType, columnBuilder);
+                      updater.updateFloat(groupId, value, columnBuilder.build(), 0);
+                    };
+                case DOUBLE ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      double value = BytesUtils.bytesToDouble(bytes, 0);
+                      readMaxMinByXFromBytes(bytes, Double.BYTES, xType, columnBuilder);
+                      updater.updateDouble(groupId, value, columnBuilder.build(), 0);
+                    };
+                case TEXT, STRING, BLOB, OBJECT ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      int length = BytesUtils.bytesToInt(bytes, 0);
+                      int xOffset = Integer.BYTES + length;
+                      Binary value = new Binary(BytesUtils.subBytes(bytes, Integer.BYTES, length));
+                      readMaxMinByXFromBytes(bytes, xOffset, xType, columnBuilder);
+                      updater.updateBinary(groupId, value, columnBuilder.build(), 0);
+                    };
+                case BOOLEAN ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      boolean value = BytesUtils.bytesToBool(bytes, 0);
+                      readMaxMinByXFromBytes(bytes, Byte.BYTES, xType, columnBuilder);
+                      updater.updateBoolean(groupId, value, columnBuilder.build(), 0);
+                    };
+                case ROW, UNKNOWN, VECTOR ->
+                    (groupId, bytes, xType, columnBuilder, updater) -> {
+                      throw unsupportedMaxMinByDataType(type);
+                    };
+              };
+
   /** Selects the type-specific decoder for a LAST intermediate value. */
   public static final TypeService<LastValueDeserializer> LAST_VALUE_DESERIALIZER_SERVICE =
       type ->
@@ -1083,11 +3036,18 @@ public class TypeServices {
     LINEAR_FILL_SERVICE.check();
     PREVIOUS_FILL_SERVICE.check();
     NEXT_FILL_SERVICE.check();
+    VALUE_FILL_SERVICE.check();
     JOIN_KEY_COMPARATOR_SERVICE.check();
     GREATEST_COLUMN_TRANSFORMER_SERVICE.check();
     LEAST_COLUMN_TRANSFORMER_SERVICE.check();
+    IN_COLUMN_VALUE_MATCHER_SERVICE.check();
+    IN_MULTI_COLUMN_TRANSFORMER_SERVICE.check();
     UPDATE_LAST_ROW_SERVICE.check();
     PRIMITIVE_COLUMN_VALUE_SETTER_SERVICE.check();
+    STATISTICS_VALUE_SETTER_SERVICE.check();
+    MIN_STATISTICS_VALUE_UPDATER_SERVICE.check();
+    MAX_STATISTICS_VALUE_UPDATER_SERVICE.check();
+    EXTREME_STATISTICS_VALUE_UPDATER_SERVICE.check();
     MAX_MIN_BY_INPUT_SERVICE.check();
     MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE.check();
     LAST_VALUE_DESERIALIZER_SERVICE.check();
@@ -1109,6 +3069,27 @@ public class TypeServices {
     DEFAULT_VALUE_WRITER_SERVICE.check();
     INTERMEDIATE_VALUE_WRITER_SERVICE.check();
     INTERMEDIATE_VALUE_INITIALIZER_SERVICE.check();
+    CAST_INPUT_SERVICE.check();
+    CAST_VALUE_SERVICE.check();
+    CAST_OBJECT_VALUE_SERVICE.check();
+    CAST_OBJECT_INPUT_SERVICE.check();
+    FORMAT_VALUE_CONVERTER_SERVICE.check();
+    GROUPED_APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE.check();
+    APPROX_MOST_FREQUENT_ACCUMULATOR_SERVICE.check();
+    APPROX_PERCENTILE_INPUT_SERVICE.check();
+    GROUPED_APPROX_PERCENTILE_INPUT_SERVICE.check();
+    GROUPED_VALUE_SERVICE.check();
+    GROUPED_INPUT_READER_SERVICE.check();
+    GROUPED_EXTREME_VALUE_SERVICE.check();
+    GROUPED_EXTREME_INPUT_READER_SERVICE.check();
+    GROUPED_TIME_INPUT_READER_SERVICE.check();
+    GROUPED_BY_INPUT_READER_SERVICE.check();
+    GROUPED_VALUE_SERIALIZER_SERVICE.check();
+    GROUPED_VALUE_SETTER_SERVICE.check();
+    GROUPED_TIME_VALUE_DESERIALIZER_SERVICE.check();
+    GROUPED_BY_VALUE_DESERIALIZER_SERVICE.check();
+    GROUPED_MAX_MIN_BY_INPUT_SERVICE.check();
+    GROUPED_MAX_MIN_BY_INTERMEDIATE_INPUT_SERVICE.check();
   }
 
   private TypeServices() {
@@ -1124,6 +3105,83 @@ public class TypeServices {
         CalcMessages.UNSUPPORTED_DATA_TYPE + type.getTypeEnum());
   }
 
+  private static UnsupportedOperationException unsupportedCastTarget(final Type type) {
+    return new UnsupportedOperationException(
+        String.format("Unsupported target dataType: %s", type.getTypeEnum()));
+  }
+
+  private static UnsupportedOperationException unsupportedCastSource(final Type type) {
+    return new UnsupportedOperationException(
+        String.format("Unsupported source dataType: %s", type.getTypeEnum()));
+  }
+
+  private static UnSupportedDataTypeException unsupportedApproxMostFrequentDataType(
+      final Type type) {
+    return new UnSupportedDataTypeException(
+            String.format(
+                CalcMessages.UNSUPPORTED_DATA_TYPE_IN_APPROX_COUNT_DISTINCT_AGGREGATION,
+                type.getTypeEnum()))
+        .setChecked(true);
+  }
+
+  private static GroupedValueService unsupportedGroupedValueService(final Type type) {
+    return new GroupedValueService() {
+      @Override
+      public void initialize(GroupedValueAccessor accessor) {
+        throw accessor.unsupportedException();
+      }
+
+      @Override
+      public void ensureCapacity(GroupedValueAccessor accessor, long groupCount) {
+        throw accessor.unsupportedException();
+      }
+
+      @Override
+      public long sizeOf(GroupedValueAccessor accessor) {
+        throw accessor.unsupportedException();
+      }
+
+      @Override
+      public void reset(GroupedValueAccessor accessor) {
+        throw accessor.unsupportedException();
+      }
+
+      @Override
+      public void write(GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder) {
+        throw accessor.unsupportedException();
+      }
+    };
+  }
+
+  private static GroupedInputReader unsupportedGroupedInputReader(final Type type) {
+    return new GroupedInputReader() {
+      @Override
+      public void addInput(
+          int[] groupIds, Column[] arguments, AggregationMask mask, GroupedInputConsumer consumer) {
+        throw unsupportedDataType(type);
+      }
+
+      @Override
+      public void addIntermediate(int[] groupIds, Column argument, GroupedInputConsumer consumer) {
+        throw unsupportedDataType(type);
+      }
+    };
+  }
+
+  private static GroupedValueSerializer unsupportedGroupedValueSerializer(final Type type) {
+    return new GroupedValueSerializer() {
+      @Override
+      public int calcTypeSize(GroupedValueAccessor accessor, long index) {
+        throw unsupportedDataType(type);
+      }
+
+      @Override
+      public void serialize(GroupedValueAccessor accessor, long index, byte[] bytes, int offset) {
+        throw unsupportedDataType(type);
+      }
+    };
+  }
+
   private static UnSupportedDataTypeException unsupportedMaxMinByDataType(final Type type) {
     return new UnSupportedDataTypeException(
         String.format(
@@ -1210,6 +3268,12 @@ public class TypeServices {
     return new Binary(BytesUtils.subBytes(bytes, offset + Integer.BYTES, length));
   }
 
+  private static Binary toBinary(Object value) {
+    return value instanceof Binary
+        ? (Binary) value
+        : new Binary(String.valueOf(value), java.nio.charset.StandardCharsets.UTF_8);
+  }
+
   private static void forEachSelectedPosition(
       Column column, AggregationMask mask, IntConsumer positionConsumer) {
     int positionCount = mask.getSelectedPositionCount();
@@ -1278,11 +3342,109 @@ public class TypeServices {
     Column apply(Column column, int rowIndex);
   }
 
+  @FunctionalInterface
+  public interface InColumnValueMatcherFactory {
+    InColumnValueMatcher create(Set<String> values);
+  }
+
+  @FunctionalInterface
+  public interface InColumnValueMatcher {
+    boolean matches(Column column, int position);
+  }
+
+  @FunctionalInterface
+  public interface InMultiColumnTransformerFactory {
+    InMultiColumnTransformer create(
+        List<ColumnTransformer> valueColumnTransformers, List<Literal> values);
+  }
+
+  @FunctionalInterface
+  public interface CastInputService {
+    void cast(
+        Column column,
+        ColumnBuilder columnBuilder,
+        int position,
+        CastValueService castValueService,
+        ZoneId zoneId);
+  }
+
+  public interface CastValueService {
+    void castInt(ColumnBuilder columnBuilder, int value);
+
+    void castDate(ColumnBuilder columnBuilder, int value, ZoneId zoneId);
+
+    void castTimestamp(ColumnBuilder columnBuilder, long value, ZoneId zoneId);
+
+    void castLong(ColumnBuilder columnBuilder, long value);
+
+    void castFloat(ColumnBuilder columnBuilder, float value);
+
+    void castDouble(ColumnBuilder columnBuilder, double value);
+
+    void castBoolean(ColumnBuilder columnBuilder, boolean value);
+
+    void castString(ColumnBuilder columnBuilder, Binary value, ZoneId zoneId);
+
+    void castBlob(ColumnBuilder columnBuilder, Binary value, ZoneId zoneId);
+
+    void castObject(ColumnBuilder columnBuilder, Binary value);
+  }
+
+  public interface CastObjectValueService {
+    Object castInt(int value);
+
+    Object castDate(int value, ZoneId zoneId);
+
+    Object castTimestamp(long value, ZoneId zoneId);
+
+    Object castLong(long value);
+
+    Object castFloat(float value);
+
+    Object castDouble(double value);
+
+    Object castBoolean(boolean value);
+
+    Object castBinary(Binary value, ZoneId zoneId);
+  }
+
+  @FunctionalInterface
+  public interface CastObjectInputService {
+    Object cast(Object value, CastObjectValueService castValueService, ZoneId zoneId);
+  }
+
+  @FunctionalInterface
+  public interface FormatValueConverter {
+    Object convert(Column column, int position, ZoneId zoneId);
+  }
+
   @FunctionalInterface
   public interface PrimitiveColumnValueSetter {
     void set(TsPrimitiveType target, Column column, int position);
   }
 
+  @FunctionalInterface
+  public interface StatisticsValueSetter {
+    void set(
+        TsPrimitiveType target,
+        Object value,
+        Supplier<? extends RuntimeException> exceptionSupplier);
+  }
+
+  @FunctionalInterface
+  public interface StatisticsValueUpdater {
+    boolean update(
+        TsPrimitiveType result,
+        Object value,
+        boolean initialized,
+        Supplier<? extends RuntimeException> exceptionSupplier);
+  }
+
+  @FunctionalInterface
+  public interface TriConsumer<A, B, C> {
+    void accept(A first, B second, C third);
+  }
+
   public interface ModeValueService {
     Object getValue(Column column, int position);
 
@@ -1570,4 +3732,178 @@ public class TypeServices {
   public interface IntermediateValueInitializer {
     void initialize(GroupedMaxMinByBaseAccumulator accumulator, boolean isX);
   }
+
+  public interface GroupedValueAccessor {
+    RuntimeException unsupportedException();
+
+    void initializeIntValues();
+
+    void initializeLongValues();
+
+    void initializeFloatValues();
+
+    void initializeDoubleValues();
+
+    void initializeBinaryValues();
+
+    void initializeBooleanValues();
+
+    IntBigArray getIntValues();
+
+    LongBigArray getLongValues();
+
+    FloatBigArray getFloatValues();
+
+    DoubleBigArray getDoubleValues();
+
+    BinaryBigArray getBinaryValues();
+
+    BooleanBigArray getBooleanValues();
+  }
+
+  public interface GroupedValueService {
+    void initialize(GroupedValueAccessor accessor);
+
+    void ensureCapacity(GroupedValueAccessor accessor, long groupCount);
+
+    long sizeOf(GroupedValueAccessor accessor);
+
+    void reset(GroupedValueAccessor accessor);
+
+    void write(GroupedValueAccessor accessor, int groupId, ColumnBuilder columnBuilder);
+  }
+
+  public interface GroupedInputConsumer {
+    void updateInt(int groupId, int value);
+
+    void updateLong(int groupId, long value);
+
+    void updateFloat(int groupId, float value);
+
+    void updateDouble(int groupId, double value);
+
+    void updateBinary(int groupId, Binary value);
+
+    void updateBoolean(int groupId, boolean value);
+  }
+
+  public interface GroupedInputReader {
+    void addInput(
+        int[] groupIds, Column[] arguments, AggregationMask mask, GroupedInputConsumer consumer);
+
+    void addIntermediate(int[] groupIds, Column argument, GroupedInputConsumer consumer);
+  }
+
+  public interface GroupedTimeInputReader {
+    void addInput(
+        int[] groupIds,
+        Column[] arguments,
+        AggregationMask mask,
+        GroupedTimeValueConsumer consumer);
+  }
+
+  public interface GroupedTimeValueConsumer {
+    void updateInt(int groupId, int value, boolean timeNull, long time);
+
+    void updateLong(int groupId, long value, boolean timeNull, long time);
+
+    void updateFloat(int groupId, float value, boolean timeNull, long time);
+
+    void updateDouble(int groupId, double value, boolean timeNull, long time);
+
+    void updateBinary(int groupId, Binary value, boolean timeNull, long time);
+
+    void updateBoolean(int groupId, boolean value, boolean timeNull, long time);
+  }
+
+  public interface GroupedByInputReader {
+    void addInput(
+        int[] groupIds, Column[] arguments, AggregationMask mask, GroupedByValueConsumer consumer);
+  }
+
+  public interface GroupedByValueConsumer {
+    void updateInt(int groupId, boolean xNull, int value, boolean timeNull, long time);
+
+    void updateLong(int groupId, boolean xNull, long value, boolean timeNull, long time);
+
+    void updateFloat(int groupId, boolean xNull, float value, boolean timeNull, long time);
+
+    void updateDouble(int groupId, boolean xNull, double value, boolean timeNull, long time);
+
+    void updateBinary(int groupId, boolean xNull, Binary value, boolean timeNull, long time);
+
+    void updateBoolean(int groupId, boolean xNull, boolean value, boolean timeNull, long time);
+  }
+
+  public interface GroupedValueSerializer {
+    int calcTypeSize(GroupedValueAccessor accessor, long index);
+
+    void serialize(GroupedValueAccessor accessor, long index, byte[] bytes, int offset);
+  }
+
+  @FunctionalInterface
+  public interface GroupedValueSetter {
+    void set(GroupedValueAccessor accessor, long index, Column column, int position);
+  }
+
+  @FunctionalInterface
+  public interface GroupedTimeValueDeserializer {
+    void deserialize(
+        byte[] bytes,
+        int offset,
+        int groupId,
+        long time,
+        boolean timeNull,
+        GroupedTimeValueConsumer consumer);
+  }
+
+  @FunctionalInterface
+  public interface GroupedByValueDeserializer {
+    void deserialize(
+        byte[] bytes,
+        int offset,
+        int groupId,
+        boolean xNull,
+        long time,
+        boolean timeNull,
+        GroupedByValueConsumer consumer);
+  }
+
+  public interface GroupedMaxMinByValueUpdater {
+    void updateInt(int groupId, int value, Column xColumn, int xIndex);
+
+    void updateLong(int groupId, long value, Column xColumn, int xIndex);
+
+    void updateFloat(int groupId, float value, Column xColumn, int xIndex);
+
+    void updateDouble(int groupId, double value, Column xColumn, int xIndex);
+
+    void updateBinary(int groupId, Binary value, Column xColumn, int xIndex);
+
+    void updateBoolean(int groupId, boolean value, Column xColumn, int xIndex);
+  }
+
+  @FunctionalInterface
+  public interface GroupedMaxMinByInput {
+    void add(
+        int[] groupIds,
+        Column[] arguments,
+        AggregationMask mask,
+        GroupedMaxMinByValueUpdater updater);
+  }
+
+  @FunctionalInterface
+  public interface GroupedMaxMinByIntermediateInput {
+    void update(
+        int groupId,
+        byte[] bytes,
+        Type xType,
+        ColumnBuilder columnBuilder,
+        GroupedMaxMinByValueUpdater updater);
+  }
+
+  @FunctionalInterface
+  public interface ValueFillFactory {
+    IFill create(Literal literal, ZoneId zoneId);
+  }
 }
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
index 4d34c865f10..ffa1f696523 100644
--- 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
@@ -38,12 +38,15 @@ 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.utils.Binary;
+import org.apache.tsfile.utils.TsPrimitiveType;
 import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -53,6 +56,34 @@ import static org.junit.Assert.assertTrue;
 
 public class TypeServicesTest {
 
+  @Test
+  public void testStatisticsValueSetterCreatesExceptionOnlyForUnsupportedType() {
+    AtomicInteger exceptionCount = new AtomicInteger();
+    RuntimeException expected = new UnsupportedOperationException("unsupported statistics type");
+    Supplier<RuntimeException> exceptionSupplier =
+        () -> {
+          exceptionCount.incrementAndGet();
+          return expected;
+        };
+    TsPrimitiveType result = Type.fromTsDataType(TSDataType.INT32).getTsPrimitiveType();
+
+    TypeServices.STATISTICS_VALUE_SETTER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.INT32))
+        .set(result, 42, exceptionSupplier);
+    assertEquals(42, result.getInt());
+    assertEquals(0, exceptionCount.get());
+
+    assertSame(
+        expected,
+        Assert.assertThrows(
+            UnsupportedOperationException.class,
+            () ->
+                TypeServices.STATISTICS_VALUE_SETTER_SERVICE
+                    .call(Type.fromTsDataType(TSDataType.VECTOR))
+                    .set(result, 42, exceptionSupplier)));
+    assertEquals(1, exceptionCount.get());
+  }
+
   @Test
   public void testNumericColumnToDoubleConversion() {
     assertEquals(
@@ -78,6 +109,40 @@ public class TypeServicesTest {
         0.0);
   }
 
+  @Test
+  public void testStatisticsValueUpdatersCreateExceptionOnlyForUnsupportedType() {
+    for (org.apache.tsfile.read.common.type.service.TypeService<TypeServices.StatisticsValueUpdater>
+        service :
+            List.of(
+                TypeServices.MAX_STATISTICS_VALUE_UPDATER_SERVICE,
+                TypeServices.MIN_STATISTICS_VALUE_UPDATER_SERVICE,
+                TypeServices.EXTREME_STATISTICS_VALUE_UPDATER_SERVICE)) {
+      AtomicInteger exceptionCount = new AtomicInteger();
+      RuntimeException expected = new UnsupportedOperationException("unsupported statistics type");
+      Supplier<RuntimeException> exceptionSupplier =
+          () -> {
+            exceptionCount.incrementAndGet();
+            return expected;
+          };
+      TsPrimitiveType result = Type.fromTsDataType(TSDataType.INT32).getTsPrimitiveType();
+      TypeServices.StatisticsValueUpdater updater =
+          service.call(Type.fromTsDataType(TSDataType.INT32));
+      assertTrue(updater.update(result, 42, false, exceptionSupplier));
+      assertEquals(42, result.getInt());
+      assertFalse(updater.update(result, 42, true, exceptionSupplier));
+      assertEquals(0, exceptionCount.get());
+      assertSame(
+          expected,
+          Assert.assertThrows(
+              UnsupportedOperationException.class,
+              () ->
+                  service
+                      .call(Type.fromTsDataType(TSDataType.VECTOR))
+                      .update(result, 42, true, exceptionSupplier)));
+      assertEquals(1, exceptionCount.get());
+    }
+  }
+
   @Test
   public void testNumericColumnToDoubleConversionUsesCallerException() {
     final UnsupportedOperationException expected = new UnsupportedOperationException("expected");
@@ -94,6 +159,25 @@ public class TypeServicesTest {
     assertSame(expected, actual);
   }
 
+  @Test
+  public void testInColumnValueMatcherServiceUsesTypeStrategies() {
+    TypeServices.InColumnValueMatcher matcher =
+        TypeServices.IN_COLUMN_VALUE_MATCHER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.INT32))
+            .create(Collections.singleton("2"));
+
+    Column input = new IntColumn(2, Optional.empty(), new int[] {1, 2});
+    assertFalse(matcher.matches(input, 0));
+    assertTrue(matcher.matches(input, 1));
+
+    Assert.assertThrows(
+        UnsupportedOperationException.class,
+        () ->
+            TypeServices.IN_COLUMN_VALUE_MATCHER_SERVICE
+                .call(Type.fromTsDataType(TSDataType.DATE))
+                .create(Collections.emptySet()));
+  }
+
   @Test
   public void testUnsupportedMergeSortComparatorFailsOnUse() {
     Assert.assertThrows(


(iotdb) 09/14: multiple refactors

Posted by ji...@apache.org.
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 286f049c4339f2012c94fba3207cb5558f77e627
Author: Tian Jiang <jt...@163.com>
AuthorDate: Thu Aug 27 17:00:27 2026 +0800

    multiple refactors
---
 .../org/apache/iotdb/isession/SessionDataSet.java  |  38 +------
 .../org/apache/iotdb/isession/TypeServices.java    |  65 ++++++++++++
 .../payload/SubscriptionRecordHandler.java         |  74 ++-----------
 .../session/subscription/payload/TypeServices.java | 115 +++++++++++++++++++++
 .../subscription/payload/TypeServicesTest.java     |  69 +++++++++++++
 5 files changed, 260 insertions(+), 101 deletions(-)

diff --git a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java
index 46881ce5893..5076afd52d0 100644
--- a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java
+++ b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java
@@ -30,8 +30,8 @@ import org.apache.thrift.TException;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.Field;
 import org.apache.tsfile.read.common.RowRecord;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.nio.ByteBuffer;
 import java.sql.Timestamp;
@@ -161,39 +161,9 @@ public class SessionDataSet implements ISessionDataSet {
       if (!ioTDBRpcDataSet.isNull(columnIndex)) {
         TSDataType dataType = ioTDBRpcDataSet.getDataType(columnIndex);
         field = new Field(dataType);
-        switch (dataType) {
-          case BOOLEAN:
-            boolean booleanValue = ioTDBRpcDataSet.getBoolean(columnIndex);
-            field.setBoolV(booleanValue);
-            break;
-          case INT32:
-          case DATE:
-            int intValue = ioTDBRpcDataSet.getInt(columnIndex);
-            field.setIntV(intValue);
-            break;
-          case INT64:
-          case TIMESTAMP:
-            long longValue = ioTDBRpcDataSet.getLong(columnIndex);
-            field.setLongV(longValue);
-            break;
-          case FLOAT:
-            float floatValue = ioTDBRpcDataSet.getFloat(columnIndex);
-            field.setFloatV(floatValue);
-            break;
-          case DOUBLE:
-            double doubleValue = ioTDBRpcDataSet.getDouble(columnIndex);
-            field.setDoubleV(doubleValue);
-            break;
-          case TEXT:
-          case BLOB:
-          case STRING:
-          case OBJECT:
-            field.setBinaryV(ioTDBRpcDataSet.getBinary(columnIndex));
-            break;
-          default:
-            throw new UnSupportedDataTypeException(
-                String.format("Data type %s is not supported.", dataType));
-        }
+        TypeServices.FIELD_VALUE_READER_SERVICE
+            .call(Type.fromTsDataType(dataType))
+            .read(ioTDBRpcDataSet, columnIndex, field);
       } else {
         field = new Field(null);
       }
diff --git a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/TypeServices.java b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/TypeServices.java
new file mode 100644
index 00000000000..3a74ddbd7b4
--- /dev/null
+++ b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/TypeServices.java
@@ -0,0 +1,65 @@
+/*
+ * 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.isession;
+
+import org.apache.iotdb.rpc.IoTDBRpcDataSet;
+import org.apache.iotdb.rpc.StatementExecutionException;
+
+import org.apache.tsfile.read.common.Field;
+import org.apache.tsfile.read.common.type.service.TypeService;
+import org.apache.tsfile.write.UnSupportedDataTypeException;
+
+/** Type-specific RPC result readers that preserve primitive access without intermediate boxing. */
+final class TypeServices {
+
+  static final TypeService<FieldValueReader> FIELD_VALUE_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                (dataSet, columnIndex, field) -> field.setBoolV(dataSet.getBoolean(columnIndex));
+            case INT32, DATE ->
+                (dataSet, columnIndex, field) -> field.setIntV(dataSet.getInt(columnIndex));
+            case INT64, TIMESTAMP ->
+                (dataSet, columnIndex, field) -> field.setLongV(dataSet.getLong(columnIndex));
+            case FLOAT ->
+                (dataSet, columnIndex, field) -> field.setFloatV(dataSet.getFloat(columnIndex));
+            case DOUBLE ->
+                (dataSet, columnIndex, field) -> field.setDoubleV(dataSet.getDouble(columnIndex));
+            case TEXT, BLOB, STRING, OBJECT ->
+                (dataSet, columnIndex, field) -> field.setBinaryV(dataSet.getBinary(columnIndex));
+            case ROW, UNKNOWN, VECTOR ->
+                (dataSet, columnIndex, field) -> {
+                  throw new UnSupportedDataTypeException(
+                      String.format("Data type %s is not supported.", type.getTypeEnum()));
+                };
+          };
+
+  static {
+    FIELD_VALUE_READER_SERVICE.check();
+  }
+
+  private TypeServices() {}
+
+  @FunctionalInterface
+  interface FieldValueReader {
+
+    void read(IoTDBRpcDataSet dataSet, int columnIndex, Field field)
+        throws StatementExecutionException;
+  }
+}
diff --git a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/SubscriptionRecordHandler.java b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/SubscriptionRecordHandler.java
index 69bd24d7687..bd51bfa87d6 100644
--- a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/SubscriptionRecordHandler.java
+++ b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/SubscriptionRecordHandler.java
@@ -26,18 +26,15 @@ import org.apache.thrift.annotation.Nullable;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.Field;
 import org.apache.tsfile.read.common.RowRecord;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.read.query.dataset.AbstractResultSet;
 import org.apache.tsfile.read.query.dataset.ResultSet;
-import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.BitMap;
-import org.apache.tsfile.utils.DateUtils;
-import org.apache.tsfile.write.UnSupportedDataTypeException;
 import org.apache.tsfile.write.record.TSRecord;
 import org.apache.tsfile.write.record.Tablet;
 import org.apache.tsfile.write.schema.IMeasurementSchema;
 
 import java.io.IOException;
-import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -343,39 +340,9 @@ public class SubscriptionRecordHandler implements Iterable<ResultSet>, Subscript
         final String measurement = currentTablet.getSchemas().get(columnIndex).getMeasurementName();
         final TSDataType dataType = currentTablet.getSchemas().get(columnIndex).getType();
         final Object value = currentTablet.getValues()[columnIndex];
-        switch (dataType) {
-          case BOOLEAN:
-            record.addPoint(measurement, ((boolean[]) value)[currentRowIndex]);
-            break;
-          case INT32:
-            record.addPoint(measurement, ((int[]) value)[currentRowIndex]);
-            break;
-          case DATE:
-            record.addPoint(measurement, ((LocalDate[]) value)[currentRowIndex]);
-            break;
-          case INT64:
-          case TIMESTAMP:
-            record.addPoint(measurement, ((long[]) value)[currentRowIndex]);
-            break;
-          case FLOAT:
-            record.addPoint(measurement, ((float[]) value)[currentRowIndex]);
-            break;
-          case DOUBLE:
-            record.addPoint(measurement, ((double[]) value)[currentRowIndex]);
-            break;
-          case TEXT:
-          case STRING:
-          case BLOB:
-          case OBJECT:
-            final Binary binary = ((Binary[]) value)[currentRowIndex];
-            if (Objects.nonNull(binary)) {
-              record.addPoint(measurement, binary.getValues());
-            }
-            break;
-          default:
-            throw new UnSupportedDataTypeException(
-                String.format("Data type %s is not supported.", dataType));
-        }
+        TypeServices.TS_RECORD_VALUE_APPENDER_SERVICE
+            .call(Type.fromTsDataType(dataType))
+            .append(record, measurement, value, currentRowIndex);
       }
       return record;
     }
@@ -383,36 +350,9 @@ public class SubscriptionRecordHandler implements Iterable<ResultSet>, Subscript
     private static Field generateFieldFromTabletValue(
         final TSDataType dataType, final Object value, final int index) {
       final Field field = new Field(dataType);
-      switch (dataType) {
-        case BOOLEAN:
-          field.setBoolV(((boolean[]) value)[index]);
-          break;
-        case INT32:
-          field.setIntV(((int[]) value)[index]);
-          break;
-        case DATE:
-          field.setIntV(DateUtils.parseDateExpressionToInt(((LocalDate[]) value)[index]));
-          break;
-        case INT64:
-        case TIMESTAMP:
-          field.setLongV(((long[]) value)[index]);
-          break;
-        case FLOAT:
-          field.setFloatV(((float[]) value)[index]);
-          break;
-        case DOUBLE:
-          field.setDoubleV(((double[]) value)[index]);
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          field.setBinaryV(new Binary((((Binary[]) value)[index]).getValues()));
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format("Data type %s is not supported.", dataType));
-      }
+      TypeServices.FIELD_VALUE_READER_SERVICE
+          .call(Type.fromTsDataType(dataType))
+          .read(field, value, index);
       return field;
     }
 
diff --git a/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/TypeServices.java b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/TypeServices.java
new file mode 100644
index 00000000000..813d500ab70
--- /dev/null
+++ b/iotdb-client/subscription/src/main/java/org/apache/iotdb/session/subscription/payload/TypeServices.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.session.subscription.payload;
+
+import org.apache.tsfile.read.common.Field;
+import org.apache.tsfile.read.common.type.service.TypeService;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.DateUtils;
+import org.apache.tsfile.write.UnSupportedDataTypeException;
+import org.apache.tsfile.write.record.TSRecord;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+/**
+ * Type-specific Tablet readers that preserve primitive-array access without intermediate boxing.
+ */
+final class TypeServices {
+
+  static final TypeService<TSRecordValueAppender> TS_RECORD_VALUE_APPENDER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((boolean[]) values)[rowIndex]);
+            case INT32 ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((int[]) values)[rowIndex]);
+            case DATE ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((LocalDate[]) values)[rowIndex]);
+            case INT64, TIMESTAMP ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((long[]) values)[rowIndex]);
+            case FLOAT ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((float[]) values)[rowIndex]);
+            case DOUBLE ->
+                (record, measurement, values, rowIndex) ->
+                    record.addPoint(measurement, ((double[]) values)[rowIndex]);
+            case TEXT, STRING, BLOB, OBJECT ->
+                (record, measurement, values, rowIndex) -> {
+                  Binary binary = ((Binary[]) values)[rowIndex];
+                  if (Objects.nonNull(binary)) {
+                    record.addPoint(measurement, binary.getValues());
+                  }
+                };
+            case ROW, UNKNOWN, VECTOR ->
+                (record, measurement, values, rowIndex) -> {
+                  throw unsupportedDataType(type.getTypeEnum());
+                };
+          };
+
+  static final TypeService<FieldValueReader> FIELD_VALUE_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> (field, values, index) -> field.setBoolV(((boolean[]) values)[index]);
+            case INT32 -> (field, values, index) -> field.setIntV(((int[]) values)[index]);
+            case DATE ->
+                (field, values, index) ->
+                    field.setIntV(
+                        DateUtils.parseDateExpressionToInt(((LocalDate[]) values)[index]));
+            case INT64, TIMESTAMP ->
+                (field, values, index) -> field.setLongV(((long[]) values)[index]);
+            case FLOAT -> (field, values, index) -> field.setFloatV(((float[]) values)[index]);
+            case DOUBLE -> (field, values, index) -> field.setDoubleV(((double[]) values)[index]);
+            case TEXT, STRING, BLOB, OBJECT ->
+                (field, values, index) ->
+                    field.setBinaryV(new Binary(((Binary[]) values)[index].getValues()));
+            case ROW, UNKNOWN, VECTOR ->
+                (field, values, index) -> {
+                  throw unsupportedDataType(type.getTypeEnum());
+                };
+          };
+
+  static {
+    TS_RECORD_VALUE_APPENDER_SERVICE.check();
+    FIELD_VALUE_READER_SERVICE.check();
+  }
+
+  private TypeServices() {}
+
+  private static UnSupportedDataTypeException unsupportedDataType(Object dataType) {
+    return new UnSupportedDataTypeException(
+        String.format("Data type %s is not supported.", dataType));
+  }
+
+  @FunctionalInterface
+  interface TSRecordValueAppender {
+
+    void append(TSRecord record, String measurement, Object values, int rowIndex);
+  }
+
+  @FunctionalInterface
+  interface FieldValueReader {
+
+    void read(Field field, Object values, int index);
+  }
+}
diff --git a/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/payload/TypeServicesTest.java b/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/payload/TypeServicesTest.java
new file mode 100644
index 00000000000..718e4b65339
--- /dev/null
+++ b/iotdb-client/subscription/src/test/java/org/apache/iotdb/session/subscription/payload/TypeServicesTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.session.subscription.payload;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.Field;
+import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.DateUtils;
+import org.apache.tsfile.write.record.TSRecord;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.time.LocalDate;
+
+public class TypeServicesTest {
+
+  @Test
+  public void testAppendTabletValuesToTsRecord() {
+    TSRecord record = new TSRecord("root.sg.d", 1);
+
+    TypeServices.TS_RECORD_VALUE_APPENDER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.INT32))
+        .append(record, "int", new int[] {42}, 0);
+    TypeServices.TS_RECORD_VALUE_APPENDER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.DATE))
+        .append(record, "date", new LocalDate[] {LocalDate.of(2024, 8, 1)}, 0);
+    TypeServices.TS_RECORD_VALUE_APPENDER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.BLOB))
+        .append(record, "nullBlob", new Binary[] {null}, 0);
+
+    Assert.assertEquals(2, record.dataPointList.size());
+    Assert.assertEquals(42, record.dataPointList.get(0).getValue());
+    Assert.assertEquals(20240801, record.dataPointList.get(1).getValue());
+  }
+
+  @Test
+  public void testReadTabletValuesIntoField() {
+    Field dateField = new Field(TSDataType.DATE);
+    TypeServices.FIELD_VALUE_READER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.DATE))
+        .read(dateField, new LocalDate[] {LocalDate.of(2024, 8, 1)}, 0);
+    Assert.assertEquals(
+        DateUtils.parseDateExpressionToInt(LocalDate.of(2024, 8, 1)).intValue(),
+        dateField.getIntV());
+
+    Field blobField = new Field(TSDataType.BLOB);
+    TypeServices.FIELD_VALUE_READER_SERVICE
+        .call(Type.fromTsDataType(TSDataType.BLOB))
+        .read(blobField, new Binary[] {new Binary(new byte[] {1, 2, 3})}, 0);
+    Assert.assertArrayEquals(new byte[] {1, 2, 3}, blobField.getBinaryV().getValues());
+  }
+}


(iotdb) 08/14: multiple refactors

Posted by ji...@apache.org.
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 814cb3c90e7843126e932bec101dc463e3e2a87e
Author: Tian Jiang <jt...@163.com>
AuthorDate: Thu Aug 27 14:53:34 2026 +0800

    multiple refactors
---
 .../java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java | 96 +++++-----------------
 .../java/org/apache/iotdb/rpc/TypeServices.java    | 94 +++++++++++++++++++++
 .../iotdb/rpc/stmt/PreparedParameterSerde.java     | 41 ++++-----
 .../org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java | 70 ++++++++++++++++
 .../iotdb/rpc/stmt/PreparedParameterSerdeTest.java | 12 +++
 5 files changed, 219 insertions(+), 94 deletions(-)

diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
index 42bf974a3eb..786847b8eac 100644
--- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
@@ -27,12 +27,12 @@ import org.apache.iotdb.service.rpc.thrift.TSFetchResultsReq;
 import org.apache.iotdb.service.rpc.thrift.TSFetchResultsResp;
 
 import org.apache.thrift.TException;
-import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.block.TsBlock;
 import org.apache.tsfile.read.common.block.column.TsBlockSerde;
+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 java.nio.ByteBuffer;
@@ -473,37 +473,10 @@ public class IoTDBRpcDataSet {
       return null;
     }
     lastReadWasNull = false;
-    TSDataType tsDataType = getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex);
-    switch (tsDataType) {
-      case BOOLEAN:
-      case INT32:
-      case INT64:
-      case FLOAT:
-      case DOUBLE:
-        return curTsBlock.getColumn(tsBlockColumnIndex).getObject(tsBlockIndex);
-      case TIMESTAMP:
-        long timestamp =
-            (tsBlockColumnIndex == -1
-                ? curTsBlock.getTimeByIndex(tsBlockIndex)
-                : curTsBlock.getColumn(tsBlockColumnIndex).getLong(tsBlockIndex));
-        return convertToTimestamp(timestamp, timeFactor);
-      case TEXT:
-      case STRING:
-        return curTsBlock
-            .getColumn(tsBlockColumnIndex)
-            .getBinary(tsBlockIndex)
-            .getStringValue(TSFileConfig.STRING_CHARSET);
-      case OBJECT:
-        return BytesUtils.parseObjectByteArrayToString(
-            curTsBlock.getColumn(tsBlockColumnIndex).getBinary(tsBlockIndex).getValues());
-      case BLOB:
-        return BytesUtils.parseBlobByteArrayToString(
-            curTsBlock.getColumn(tsBlockColumnIndex).getBinary(tsBlockIndex).getValues());
-      case DATE:
-        return DateUtils.formatDate(curTsBlock.getColumn(tsBlockColumnIndex).getInt(tsBlockIndex));
-      default:
-        return null;
-    }
+    Type type = Type.fromTsDataType(getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex));
+    return TypeServices.RPC_OBJECT_READER_SERVICE
+        .call(type)
+        .read(type, getColumnByTsBlockColumnIndex(tsBlockColumnIndex), tsBlockIndex, timeFactor);
   }
 
   public String getString(int columnIndex) throws StatementExecutionException {
@@ -526,47 +499,22 @@ public class IoTDBRpcDataSet {
       return null;
     }
     lastReadWasNull = false;
-    return getString(tsBlockColumnIndex, getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex));
-  }
-
-  private String getString(int index, TSDataType tsDataType) {
-    switch (tsDataType) {
-      case BOOLEAN:
-        return String.valueOf(curTsBlock.getColumn(index).getBoolean(tsBlockIndex));
-      case INT32:
-        return String.valueOf(curTsBlock.getColumn(index).getInt(tsBlockIndex));
-      case INT64:
-        return String.valueOf(
-            (index == -1
-                ? curTsBlock.getTimeByIndex(tsBlockIndex)
-                : curTsBlock.getColumn(index).getLong(tsBlockIndex)));
-      case TIMESTAMP:
-        long timestamp =
-            (index == -1
-                ? curTsBlock.getTimeByIndex(tsBlockIndex)
-                : curTsBlock.getColumn(index).getLong(tsBlockIndex));
-        return RpcUtils.formatDatetime(timeFormat, timePrecision, timestamp, zoneId);
-      case FLOAT:
-        return String.valueOf(curTsBlock.getColumn(index).getFloat(tsBlockIndex));
-      case DOUBLE:
-        return String.valueOf(curTsBlock.getColumn(index).getDouble(tsBlockIndex));
-      case TEXT:
-      case STRING:
-        return curTsBlock
-            .getColumn(index)
-            .getBinary(tsBlockIndex)
-            .getStringValue(TSFileConfig.STRING_CHARSET);
-      case OBJECT:
-        return BytesUtils.parseObjectByteArrayToString(
-            curTsBlock.getColumn(index).getBinary(tsBlockIndex).getValues());
-      case BLOB:
-        return BytesUtils.parseBlobByteArrayToString(
-            curTsBlock.getColumn(index).getBinary(tsBlockIndex).getValues());
-      case DATE:
-        return DateUtils.formatDate(curTsBlock.getColumn(index).getInt(tsBlockIndex));
-      default:
-        return null;
-    }
+    Type type = Type.fromTsDataType(getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex));
+    return TypeServices.RPC_STRING_READER_SERVICE
+        .call(type)
+        .read(
+            type,
+            getColumnByTsBlockColumnIndex(tsBlockColumnIndex),
+            tsBlockIndex,
+            timeFormat,
+            timePrecision,
+            zoneId);
+  }
+
+  private Column getColumnByTsBlockColumnIndex(int tsBlockColumnIndex) {
+    return tsBlockColumnIndex == -1
+        ? curTsBlock.getTimeColumn()
+        : curTsBlock.getColumn(tsBlockColumnIndex);
   }
 
   public Timestamp getTimestamp(int columnIndex) throws StatementExecutionException {
diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
index 4e1cec95c6f..4b031124372 100644
--- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
@@ -18,6 +18,9 @@
 
 package org.apache.iotdb.rpc;
 
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.common.conf.TSFileConfig;
+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.BytesUtils;
@@ -25,6 +28,7 @@ import org.apache.tsfile.utils.DateUtils;
 
 import java.nio.charset.StandardCharsets;
 import java.sql.Timestamp;
+import java.time.ZoneId;
 import java.util.function.Function;
 
 final class TypeServices {
@@ -59,10 +63,100 @@ final class TypeServices {
             case ROW, UNKNOWN, VECTOR -> ignored -> null;
           };
 
+  static final TypeService<RpcObjectReader> RPC_OBJECT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN, INT32, INT64, FLOAT, DOUBLE ->
+                (actualType, column, position, timeFactor) ->
+                    actualType.getObject(column, position);
+            case TIMESTAMP ->
+                (actualType, column, position, timeFactor) ->
+                    RpcUtils.convertToTimestamp(actualType.getLong(column, position), timeFactor);
+            case TEXT, STRING ->
+                (actualType, column, position, timeFactor) ->
+                    actualType
+                        .getBinary(column, position)
+                        .getStringValue(TSFileConfig.STRING_CHARSET);
+            case OBJECT ->
+                (actualType, column, position, timeFactor) ->
+                    BytesUtils.parseObjectByteArrayToString(
+                        actualType.getBinary(column, position).getValues());
+            case BLOB ->
+                (actualType, column, position, timeFactor) ->
+                    BytesUtils.parseBlobByteArrayToString(
+                        actualType.getBinary(column, position).getValues());
+            case DATE ->
+                (actualType, column, position, timeFactor) ->
+                    DateUtils.formatDate(actualType.getInt(column, position));
+            case ROW, UNKNOWN, VECTOR -> (actualType, column, position, timeFactor) -> null;
+          };
+
+  static final TypeService<RpcStringReader> RPC_STRING_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    String.valueOf(actualType.getBoolean(column, position));
+            case INT32 ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    String.valueOf(actualType.getInt(column, position));
+            case INT64 ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    String.valueOf(actualType.getLong(column, position));
+            case FLOAT ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    String.valueOf(actualType.getFloat(column, position));
+            case DOUBLE ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    String.valueOf(actualType.getDouble(column, position));
+            case TIMESTAMP ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    RpcUtils.formatDatetime(
+                        timeFormat, timePrecision, actualType.getLong(column, position), zoneId);
+            case TEXT, STRING ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    actualType
+                        .getBinary(column, position)
+                        .getStringValue(TSFileConfig.STRING_CHARSET);
+            case OBJECT ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    BytesUtils.parseObjectByteArrayToString(
+                        actualType.getBinary(column, position).getValues());
+            case BLOB ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    BytesUtils.parseBlobByteArrayToString(
+                        actualType.getBinary(column, position).getValues());
+            case DATE ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) ->
+                    DateUtils.formatDate(actualType.getInt(column, position));
+            case ROW, UNKNOWN, VECTOR ->
+                (actualType, column, position, timeFormat, timePrecision, zoneId) -> null;
+          };
+
   static {
     JDBC_STRING_READER_SERVICE.check();
     JDBC_OBJECT_READER_SERVICE.check();
+    RPC_OBJECT_READER_SERVICE.check();
+    RPC_STRING_READER_SERVICE.check();
   }
 
   private TypeServices() {}
+
+  @FunctionalInterface
+  interface RpcObjectReader {
+
+    Object read(Type type, Column column, int position, int timeFactor);
+  }
+
+  @FunctionalInterface
+  interface RpcStringReader {
+
+    String read(
+        Type type,
+        Column column,
+        int position,
+        String timeFormat,
+        String timePrecision,
+        ZoneId zoneId);
+  }
 }
diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java
index e42710d23e1..6bf9cbba226 100644
--- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java
@@ -21,10 +21,13 @@ package org.apache.iotdb.rpc.stmt;
 
 import org.apache.iotdb.rpc.i18n.RpcMessages;
 
+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.utils.Binary;
 import org.apache.tsfile.utils.PublicBAOS;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
+import org.apache.tsfile.utils.TsPrimitiveType;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -145,27 +148,25 @@ public class PreparedParameterSerde {
   }
 
   private static Object deserializeValue(ByteBuffer buffer, TSDataType type) {
-    switch (type) {
-      case UNKNOWN:
-        return null;
-      case BOOLEAN:
-        return ReadWriteIOUtils.readBool(buffer);
-      case INT32:
-        return ReadWriteIOUtils.readInt(buffer);
-      case INT64:
-        return ReadWriteIOUtils.readLong(buffer);
-      case FLOAT:
-        return ReadWriteIOUtils.readFloat(buffer);
-      case DOUBLE:
-        return ReadWriteIOUtils.readDouble(buffer);
-      case TEXT:
-      case STRING:
-        return ReadWriteIOUtils.readString(buffer);
-      case BLOB:
-        return ReadWriteIOUtils.readBinary(buffer).getValues();
-      default:
-        throw new IllegalArgumentException(RpcMessages.UNSUPPORTED_TYPE + type);
+    if (type == TSDataType.UNKNOWN) {
+      return null;
+    }
+    if (!type.isNumeric()
+        && type != TSDataType.BOOLEAN
+        && type != TSDataType.TEXT
+        && type != TSDataType.STRING
+        && type != TSDataType.BLOB) {
+      throw new IllegalArgumentException(RpcMessages.UNSUPPORTED_TYPE + type);
+    }
+
+    TsPrimitiveType value = Type.fromTsDataType(type).deserialize(buffer);
+    if (type == TSDataType.BLOB) {
+      return value.getBinary().getValues();
+    }
+    if (type == TSDataType.TEXT || type == TSDataType.STRING) {
+      return value.getBinary().getStringValue(TSFileConfig.STRING_CHARSET);
     }
+    return value.getValue();
   }
 
   /** Convert byte array to hexadecimal string representation. */
diff --git a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
index af98f2a60ec..ccadd0d51d5 100644
--- a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
+++ b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
@@ -18,15 +18,20 @@
 
 package org.apache.iotdb.rpc;
 
+import org.apache.tsfile.block.column.Column;
+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.UnknownType;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.BytesUtils;
+import org.apache.tsfile.utils.DateUtils;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.nio.ByteBuffer;
+import java.time.ZoneId;
 import java.util.Arrays;
 
 public class IoTDBJDBCDataSetTest {
@@ -93,6 +98,71 @@ public class IoTDBJDBCDataSetTest {
         TypeServices.JDBC_OBJECT_READER_SERVICE.call(UnknownType.UNKNOWN).apply(new byte[0]));
   }
 
+  @Test
+  public void testRpcValueReadersUseTypeServices() {
+    Type intType = Type.fromTsDataType(TSDataType.INT32);
+    Column intColumn = intType.createColumnBuilder(1).writeInt(42).build();
+    Assert.assertEquals(42, readRpcObject(intType, intColumn, 1_000));
+    Assert.assertEquals("42", readRpcString(intType, intColumn));
+
+    Type textType = Type.fromTsDataType(TSDataType.TEXT);
+    Column textColumn =
+        textType
+            .createColumnBuilder(1)
+            .writeBinary(new Binary("text", TSFileConfig.STRING_CHARSET))
+            .build();
+    Assert.assertEquals("text", readRpcObject(textType, textColumn, 1_000));
+    Assert.assertEquals("text", readRpcString(textType, textColumn));
+
+    Type timestampType = Type.fromTsDataType(TSDataType.TIMESTAMP);
+    Column timestampColumn = timestampType.createColumnBuilder(1).writeLong(1_234_567).build();
+    Assert.assertEquals(
+        RpcUtils.convertToTimestamp(1_234_567, 1_000_000),
+        readRpcObject(timestampType, timestampColumn, 1_000_000));
+    Assert.assertEquals("1234567", readRpcString(timestampType, timestampColumn));
+
+    Type dateType = Type.fromTsDataType(TSDataType.DATE);
+    Column dateColumn = dateType.createColumnBuilder(1).writeInt(20240801).build();
+    Assert.assertEquals(DateUtils.formatDate(20240801), readRpcObject(dateType, dateColumn, 1_000));
+    Assert.assertEquals(DateUtils.formatDate(20240801), readRpcString(dateType, dateColumn));
+
+    byte[] blob = new byte[] {1, 2, 3};
+    Type blobType = Type.fromTsDataType(TSDataType.BLOB);
+    Column blobColumn = blobType.createColumnBuilder(1).writeBinary(new Binary(blob)).build();
+    Assert.assertEquals(
+        BytesUtils.parseBlobByteArrayToString(blob), readRpcObject(blobType, blobColumn, 1_000));
+    Assert.assertEquals(
+        BytesUtils.parseBlobByteArrayToString(blob), readRpcString(blobType, blobColumn));
+
+    byte[] object = ByteBuffer.allocate(16).putLong(0).putLong(42).array();
+    Type objectType = Type.fromTsDataType(TSDataType.OBJECT);
+    Column objectColumn = objectType.createColumnBuilder(1).writeBinary(new Binary(object)).build();
+    Assert.assertEquals(
+        BytesUtils.parseObjectByteArrayToString(object),
+        readRpcObject(objectType, objectColumn, 1_000));
+    Assert.assertEquals(
+        BytesUtils.parseObjectByteArrayToString(object), readRpcString(objectType, objectColumn));
+
+    Assert.assertNull(
+        TypeServices.RPC_OBJECT_READER_SERVICE
+            .call(UnknownType.UNKNOWN)
+            .read(UnknownType.UNKNOWN, null, 0, 1_000));
+    Assert.assertNull(
+        TypeServices.RPC_STRING_READER_SERVICE
+            .call(UnknownType.UNKNOWN)
+            .read(UnknownType.UNKNOWN, null, 0, "long", "ms", ZoneId.of("UTC")));
+  }
+
+  private static Object readRpcObject(Type type, Column column, int timeFactor) {
+    return TypeServices.RPC_OBJECT_READER_SERVICE.call(type).read(type, column, 0, timeFactor);
+  }
+
+  private static String readRpcString(Type type, Column column) {
+    return TypeServices.RPC_STRING_READER_SERVICE
+        .call(type)
+        .read(type, column, 0, "long", "ms", ZoneId.of("UTC"));
+  }
+
   private static void assertUnsupported(TSDataType dataType) {
     UnSupportedDataTypeException exception =
         Assert.assertThrows(
diff --git a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerdeTest.java b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerdeTest.java
index cd3929c8224..20de227def4 100644
--- a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerdeTest.java
+++ b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerdeTest.java
@@ -33,6 +33,7 @@ import static org.apache.iotdb.rpc.stmt.PreparedParameterSerde.serialize;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 /** Unit tests for {@link PreparedParameterSerde}. */
@@ -124,4 +125,15 @@ public class PreparedParameterSerdeTest {
     buffer.flip();
     deserialize(buffer);
   }
+
+  @Test
+  public void testUnsupportedType() {
+    ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + Byte.BYTES + Integer.BYTES);
+    buffer.putInt(1);
+    TSDataType.DATE.serializeTo(buffer);
+    buffer.putInt(20240801);
+    buffer.flip();
+
+    assertThrows(IllegalArgumentException.class, () -> deserialize(buffer));
+  }
 }


(iotdb) 05/14: multiple refactors

Posted by ji...@apache.org.
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 652cb02d0144233c2e87944645bbe88678b289ee
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 14:32:14 2026 +0800

    multiple refactors
---
 .../relational/MergeSortFullOuterJoinOperator.java |  6 +--
 .../calc/plan/planner/TableOperatorGenerator.java  | 46 ++--------------------
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 19 +++++++++
 .../apache/iotdb/calc/utils/TypeServicesTest.java  | 20 ++++++++++
 4 files changed, 46 insertions(+), 45 deletions(-)

diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MergeSortFullOuterJoinOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MergeSortFullOuterJoinOperator.java
index 278d87ea021..cfb2c587a40 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MergeSortFullOuterJoinOperator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MergeSortFullOuterJoinOperator.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.calc.execution.operator.CommonOperatorContext;
 import org.apache.iotdb.calc.execution.operator.Operator;
 import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator;
 import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper;
 
 import org.apache.tsfile.block.column.Column;
@@ -32,7 +33,6 @@ import org.apache.tsfile.read.common.block.TsBlock;
 import org.apache.tsfile.utils.RamUsageEstimator;
 
 import java.util.List;
-import java.util.function.BiFunction;
 
 public class MergeSortFullOuterJoinOperator extends AbstractMergeSortJoinOperator {
   private static final long INSTANCE_SIZE =
@@ -41,7 +41,7 @@ public class MergeSortFullOuterJoinOperator extends AbstractMergeSortJoinOperato
   // stores last row matched join criteria, only used in outer join
   private TsBlock lastMatchedRightBlock = null;
   private final int[] lastMatchedBlockPositions;
-  private final List<BiFunction<Column, Integer, Column>> updateLastMatchedRowFunctions;
+  private final List<TypeServices.ColumnRowFunction> updateLastMatchedRowFunctions;
 
   public MergeSortFullOuterJoinOperator(
       CommonOperatorContext operatorContext,
@@ -53,7 +53,7 @@ public class MergeSortFullOuterJoinOperator extends AbstractMergeSortJoinOperato
       int[] rightOutputSymbolIdx,
       List<JoinKeyComparator> joinKeyComparators,
       List<TSDataType> dataTypes,
-      List<BiFunction<Column, Integer, Column>> updateLastMatchedRowFunctions) {
+      List<TypeServices.ColumnRowFunction> updateLastMatchedRowFunctions) {
     super(
         operatorContext,
         leftChild,
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
index 86786404bc4..8b66d4eef27 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java
@@ -108,6 +108,7 @@ import org.apache.iotdb.calc.plan.relational.planner.CastToStringLiteralVisitor;
 import org.apache.iotdb.calc.plan.relational.planner.CastToTimestampLiteralVisitor;
 import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer;
 import org.apache.iotdb.calc.transformation.dag.column.leaf.LeafColumnTransformer;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.calc.utils.datastructure.SortKey;
 import org.apache.iotdb.common.rpc.thrift.TAggregationType;
 import org.apache.iotdb.commons.exception.SemanticException;
@@ -183,12 +184,6 @@ import org.apache.tsfile.common.conf.TSFileConfig;
 import org.apache.tsfile.common.conf.TSFileDescriptor;
 import org.apache.tsfile.enums.TSDataType;
 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.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.RunLengthEncodedColumn;
 import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.Binary;
@@ -205,7 +200,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.OptionalInt;
 import java.util.Set;
-import java.util.function.BiFunction;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
@@ -1175,7 +1169,9 @@ public abstract class TableOperatorGenerator<
           rightOutputSymbolIdx,
           JoinKeyComparatorFactory.getComparators(joinKeyTypes, true),
           dataTypes,
-          joinKeyTypes.stream().map(this::buildUpdateLastRowFunction).collect(Collectors.toList()));
+          joinKeyTypes.stream()
+              .map(TypeServices.UPDATE_LAST_ROW_SERVICE::call)
+              .collect(Collectors.toList()));
     } else if (requireNonNull(node.getJoinType()) == JoinNode.JoinType.LEFT) {
       CommonOperatorContext operatorContext =
           addOperatorContext(
@@ -1210,40 +1206,6 @@ public abstract class TableOperatorGenerator<
     }
   }
 
-  protected BiFunction<Column, Integer, Column> buildUpdateLastRowFunction(Type joinKeyType) {
-    switch (joinKeyType.getTypeEnum()) {
-      case INT32:
-        return (inputColumn, rowIndex) ->
-            new IntColumn(
-                1, Optional.empty(), new int[] {inputColumn.getInt(rowIndex)}, TSDataType.INT32);
-      case DATE:
-        return (inputColumn, rowIndex) ->
-            new IntColumn(
-                1, Optional.empty(), new int[] {inputColumn.getInt(rowIndex)}, TSDataType.DATE);
-      case INT64:
-      case TIMESTAMP:
-        return (inputColumn, rowIndex) ->
-            new LongColumn(1, Optional.empty(), new long[] {inputColumn.getLong(rowIndex)});
-      case FLOAT:
-        return (inputColumn, rowIndex) ->
-            new FloatColumn(1, Optional.empty(), new float[] {inputColumn.getFloat(rowIndex)});
-      case DOUBLE:
-        return (inputColumn, rowIndex) ->
-            new DoubleColumn(1, Optional.empty(), new double[] {inputColumn.getDouble(rowIndex)});
-      case BOOLEAN:
-        return (inputColumn, rowIndex) ->
-            new BooleanColumn(
-                1, Optional.empty(), new boolean[] {inputColumn.getBoolean(rowIndex)});
-      case STRING:
-      case TEXT:
-      case BLOB:
-        return (inputColumn, rowIndex) ->
-            new BinaryColumn(1, Optional.empty(), new Binary[] {inputColumn.getBinary(rowIndex)});
-      default:
-        throw new UnsupportedOperationException(CalcMessages.UNSUPPORTED_DATA_TYPE + joinKeyType);
-    }
-  }
-
   @Override
   public Operator visitSemiJoin(SemiJoinNode node, C context) {
     List<TSDataType> dataTypes = getOutputColumnTypes(node, context.getTableTypeProvider());
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 5980317b17c..19583424db4 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
@@ -339,6 +339,19 @@ public class TypeServices {
                     };
               };
 
+  /**
+   * Creates a single-row column containing the value at the requested row. Delegating the write to
+   * {@link Type} preserves type-specific column metadata (for example, DATE) without another
+   * TsDataType dispatch.
+   */
+  public static final TypeService<ColumnRowFunction> UPDATE_LAST_ROW_SERVICE =
+      type ->
+          (column, rowIndex) -> {
+            ColumnBuilder columnBuilder = type.createColumnBuilder(1);
+            type.write(columnBuilder, column, rowIndex);
+            return columnBuilder.build();
+          };
+
   public static final TypeService<Function<TsPrimitiveType, Object>>
       PRIMITIVE_TYPE_VALUE_EXTRACTOR_SERVICE =
           type ->
@@ -579,6 +592,7 @@ public class TypeServices {
     JOIN_KEY_COMPARATOR_SERVICE.check();
     GREATEST_COLUMN_TRANSFORMER_SERVICE.check();
     LEAST_COLUMN_TRANSFORMER_SERVICE.check();
+    UPDATE_LAST_ROW_SERVICE.check();
     MERGE_SORT_COMPARATOR_SERVICE.check();
     MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE.check();
     MEMORY_USAGE_OF_ONE_SERIALIZABLE_ROW_FIELD_SERVICE.check();
@@ -618,6 +632,11 @@ public class TypeServices {
     TSEncoding getDefaultTextEncoding();
   }
 
+  @FunctionalInterface
+  public interface ColumnRowFunction {
+    Column apply(Column column, int rowIndex);
+  }
+
   @FunctionalInterface
   public interface ColumnToDoubleConverter {
     double convert(Column column, int position);
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
index 39ca2a1a2b4..33d3723550c 100644
--- 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
@@ -151,6 +151,26 @@ public class TypeServicesTest {
                 .apply(columnTransformers));
   }
 
+  @Test
+  public void testUpdateLastRowServiceUsesTypeColumnWriter() {
+    Column input = new IntColumn(2, Optional.empty(), new int[] {10, 20}, TSDataType.DATE);
+    Column output =
+        TypeServices.UPDATE_LAST_ROW_SERVICE
+            .call(Type.fromTsDataType(TSDataType.DATE))
+            .apply(input, 1);
+
+    assertEquals(1, output.getPositionCount());
+    assertEquals(20, output.getInt(0));
+    assertEquals(TSDataType.DATE, output.getDataType());
+
+    Assert.assertThrows(
+        RuntimeException.class,
+        () ->
+            TypeServices.UPDATE_LAST_ROW_SERVICE
+                .call(Type.fromTsDataType(TSDataType.VECTOR))
+                .apply(input, 0));
+  }
+
   // Covers every supported RANGE-frame type and guards native integer overflow and long precision.
   @Test
   public void testRangeFrameComparatorPreservesNativeArithmetic() {


(iotdb) 03/14: multiple refactors

Posted by ji...@apache.org.
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 ec6e5de96b838c47c562c66b3049b621b0f96ee2
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 11:09:23 2026 +0800

    multiple refactors
---
 .../org/apache/iotdb/calc/utils/TypeServices.java  | 126 +++++++++++++++++++++
 1 file changed, 126 insertions(+)

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 705c1b02aa4..8377ce7518d 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
@@ -92,6 +92,118 @@ import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtil
 
 public class TypeServices {
 
+  private static final IdentityLinearFill IDENTITY_LINEAR_FILL = new IdentityLinearFill();
+
+  // Unsupported fill types defer their exception until the returned strategy is used so check()
+  // can still validate that every TypeEnum has a service entry.
+  public static final TypeService<Supplier<ILinearFill>> LINEAR_FILL_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE -> IntLinearFill::new;
+            case INT64, TIMESTAMP -> LongLinearFill::new;
+            case FLOAT -> FloatLinearFill::new;
+            case DOUBLE -> DoubleLinearFill::new;
+            case BOOLEAN, TEXT, STRING, BLOB, OBJECT -> () -> IDENTITY_LINEAR_FILL;
+            case ROW, UNKNOWN, VECTOR ->
+                () -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  public static final TypeService<Function<IFillFilter, IFill>> PREVIOUS_FILL_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN ->
+                filter ->
+                    filter == null
+                        ? new BooleanPreviousFill()
+                        : new BooleanPreviousFillWithTimeDuration(filter);
+            case TEXT, STRING, BLOB, OBJECT ->
+                filter ->
+                    filter == null
+                        ? new BinaryPreviousFill()
+                        : new BinaryPreviousFillWithTimeDuration(filter);
+            case INT32, DATE ->
+                filter ->
+                    filter == null
+                        ? new IntPreviousFill()
+                        : new IntPreviousFillWithTimeDuration(filter);
+            case INT64, TIMESTAMP ->
+                filter ->
+                    filter == null
+                        ? new LongPreviousFill()
+                        : new LongPreviousFillWithTimeDuration(filter);
+            case FLOAT ->
+                filter ->
+                    filter == null
+                        ? new FloatPreviousFill()
+                        : new FloatPreviousFillWithTimeDuration(filter);
+            case DOUBLE ->
+                filter ->
+                    filter == null
+                        ? new DoublePreviousFill()
+                        : new DoublePreviousFillWithTimeDuration(filter);
+            case ROW, UNKNOWN, VECTOR ->
+                filter -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  public static final TypeService<Function<IFillFilter, ILinearFill>> NEXT_FILL_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> BooleanNextFill::new;
+            case TEXT, STRING, BLOB, OBJECT -> BinaryNextFill::new;
+            case INT32, DATE -> IntNextFill::new;
+            case INT64, TIMESTAMP -> LongNextFill::new;
+            case FLOAT -> FloatNextFill::new;
+            case DOUBLE -> DoubleNextFill::new;
+            case ROW, UNKNOWN, VECTOR ->
+                filter -> {
+                  throw unsupportedDataType(type);
+                };
+          };
+
+  public static final TypeService<BooleanFunction<JoinKeyComparator>> JOIN_KEY_COMPARATOR_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, DATE ->
+                ascending ->
+                    ascending
+                        ? AscIntTypeJoinKeyComparator.getInstance()
+                        : DescIntTypeJoinKeyComparator.getInstance();
+            case INT64, TIMESTAMP ->
+                ascending ->
+                    ascending
+                        ? AscLongTypeJoinKeyComparator.getInstance()
+                        : DescLongTypeJoinKeyComparator.getInstance();
+            case FLOAT ->
+                ascending ->
+                    ascending
+                        ? AscFloatTypeJoinKeyComparator.getInstance()
+                        : DescFloatTypeJoinKeyComparator.getInstance();
+            case DOUBLE ->
+                ascending ->
+                    ascending
+                        ? AscDoubleTypeJoinKeyComparator.getInstance()
+                        : DescDoubleTypeJoinKeyComparator.getInstance();
+            case BOOLEAN ->
+                ascending ->
+                    ascending
+                        ? AscBooleanTypeJoinKeyComparator.getInstance()
+                        : DescBooleanTypeJoinKeyComparator.getInstance();
+            case STRING, BLOB, TEXT ->
+                ascending ->
+                    ascending
+                        ? AscBinaryTypeJoinKeyComparator.getInstance()
+                        : DescBinaryTypeJoinKeyComparator.getInstance();
+            case OBJECT, ROW, UNKNOWN, VECTOR ->
+                ascending -> {
+                  throw new UnsupportedOperationException(
+                      CalcMessages.UNSUPPORTED_DATA_TYPE + type);
+                };
+          };
+
   public static final TypeService<IntFunction<Comparator<SortKey>>> MERGE_SORT_COMPARATOR_SERVICE =
       type ->
           switch (type.getTypeEnum()) {
@@ -394,6 +506,10 @@ public class TypeServices {
               };
 
   static {
+    LINEAR_FILL_SERVICE.check();
+    PREVIOUS_FILL_SERVICE.check();
+    NEXT_FILL_SERVICE.check();
+    JOIN_KEY_COMPARATOR_SERVICE.check();
     MERGE_SORT_COMPARATOR_SERVICE.check();
     MEMORY_USAGE_OF_ONE_MERGE_SORT_KEY_SERVICE.check();
     MEMORY_USAGE_OF_ONE_SERIALIZABLE_ROW_FIELD_SERVICE.check();
@@ -410,6 +526,10 @@ public class TypeServices {
     // util class doesn't need constructor
   }
 
+  private static IllegalArgumentException unsupportedDataType(final Type type) {
+    return new IllegalArgumentException(CalcMessages.UNKNOWN_DATATYPE + type.getTypeEnum());
+  }
+
   public interface DefaultEncodingProvider {
     TSEncoding getDefaultBooleanEncoding();
 
@@ -429,6 +549,12 @@ public class TypeServices {
     double convert(Column column, int position);
   }
 
+  @FunctionalInterface
+  public interface BooleanFunction<T> {
+    // Keep primitive dispatchers unboxed when selecting ascending/descending strategies.
+    T apply(boolean value);
+  }
+
   @FunctionalInterface
   public interface ColumnToDoubleConverterFactory {
     ColumnToDoubleConverter create(Supplier<? extends RuntimeException> exceptionSupplier);


(iotdb) 11/14: multiple refactors

Posted by ji...@apache.org.
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 58a688e8f831c1c8bfb95d16fe5f068a14d78926
Author: Tian Jiang <jt...@163.com>
AuthorDate: Thu Aug 27 19:08:13 2026 +0800

    multiple refactors
---
 .../iotdb/commons/udf/builtin/TypeServices.java    |  51 +++++++
 .../iotdb/commons/udf/builtin/UDTFInRange.java     | 162 ++-------------------
 .../apache/iotdb/commons/udf/builtin/UDTFMath.java | 149 ++-----------------
 .../iotdb/commons/udf/builtin/UDTFOnOff.java       | 153 ++-----------------
 4 files changed, 92 insertions(+), 423 deletions(-)

diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
index f663171abae..2957e953ea2 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/TypeServices.java
@@ -18,10 +18,13 @@
 
 package org.apache.iotdb.commons.udf.builtin;
 
+import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
 import org.apache.iotdb.udf.api.access.Row;
 import org.apache.iotdb.udf.api.collector.PointCollector;
 import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
+import org.apache.iotdb.udf.api.type.Type;
 
+import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.read.common.type.service.TypeService;
 
 import java.io.IOException;
@@ -175,16 +178,54 @@ final class TypeServices {
                 };
           };
 
+  // UDF Row has no generic numeric accessor, so bind its primitive getter once during beforeStart.
+  static final TypeService<NumericRowReader> NUMERIC_ROW_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32 -> row -> row.getInt(0);
+            case INT64 -> row -> row.getLong(0);
+            case FLOAT -> row -> row.getFloat(0);
+            case DOUBLE -> row -> row.getDouble(0);
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                row -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
+  // TsFile Type provides primitive numeric conversion for every supported column implementation.
+  static final TypeService<NumericColumnReader> NUMERIC_COLUMN_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case INT32, INT64, FLOAT, DOUBLE -> type::getDouble;
+            case BOOLEAN, DATE, TIMESTAMP, TEXT, STRING, BLOB, OBJECT, ROW, UNKNOWN, VECTOR ->
+                (column, position) -> {
+                  throw invalidNumericDataType(type);
+                };
+          };
+
   static {
     VALUE_TREND_READER_SERVICE.check();
     VALUE_DIFFERENCE_OPERATOR_SERVICE.check();
     NON_NEGATIVE_VALUE_DIFFERENCE_OPERATOR_SERVICE.check();
     DERIVATIVE_OPERATOR_SERVICE.check();
     NON_NEGATIVE_DERIVATIVE_OPERATOR_SERVICE.check();
+    NUMERIC_ROW_READER_SERVICE.check();
+    NUMERIC_COLUMN_READER_SERVICE.check();
   }
 
   private TypeServices() {}
 
+  private static UDFInputSeriesDataTypeNotValidException invalidNumericDataType(
+      org.apache.tsfile.read.common.type.Type type) {
+    return new UDFInputSeriesDataTypeNotValidException(
+        0,
+        UDFDataTypeTransformer.transformReadTypeToUDFDataType(type),
+        Type.INT32,
+        Type.INT64,
+        Type.FLOAT,
+        Type.DOUBLE);
+  }
+
   @FunctionalInterface
   interface PreviousValueReader {
     void read(UDTFValueTrend target, Row row)
@@ -203,4 +244,14 @@ final class TypeServices {
         UDTFValueTrend target, long time, Row row, PointCollector collector, double timeDelta)
         throws UDFInputSeriesDataTypeNotValidException, IOException;
   }
+
+  @FunctionalInterface
+  interface NumericRowReader {
+    double read(Row row) throws UDFInputSeriesDataTypeNotValidException, IOException;
+  }
+
+  @FunctionalInterface
+  interface NumericColumnReader {
+    double read(Column column, int position) throws UDFInputSeriesDataTypeNotValidException;
+  }
 }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java
index 4c17a270697..4b3299f538c 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java
@@ -29,19 +29,18 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.MappableRowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
-import org.apache.tsfile.enums.TSDataType;
 
 import java.io.IOException;
 
 public class UDTFInRange implements UDTF {
-  protected TSDataType dataType;
   protected double upper;
   protected double lower;
+  private TypeServices.NumericRowReader rowReader;
+  private TypeServices.NumericColumnReader columnReader;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -62,46 +61,20 @@ public class UDTFInRange implements UDTF {
       throws MetadataException {
     upper = parameters.getDouble("upper");
     lower = parameters.getDouble("lower");
-    dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    org.apache.tsfile.read.common.type.Type type =
+        UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0));
+    rowReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(type);
+    columnReader = TypeServices.NUMERIC_COLUMN_READER_SERVICE.call(type);
     configurations
         .setAccessStrategy(new MappableRowByRowAccessStrategy())
         .setOutputDataType(Type.BOOLEAN);
   }
 
   @Override
-  public void transform(Row row, PointCollector collector)
-      throws UDFInputSeriesDataTypeNotValidException, IOException {
+  public void transform(Row row, PointCollector collector) throws IOException {
     long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        collector.putBoolean(time, row.getInt(0) >= lower && upper >= row.getInt(0));
-        break;
-      case INT64:
-        collector.putBoolean(time, row.getLong(0) >= lower && upper >= row.getLong(0));
-        break;
-      case FLOAT:
-        collector.putBoolean(time, row.getFloat(0) >= lower && upper >= row.getFloat(0));
-        break;
-      case DOUBLE:
-        collector.putBoolean(time, row.getDouble(0) >= lower && upper >= row.getDouble(0));
-        break;
-      case BLOB:
-      case OBJECT:
-      case TEXT:
-      case DATE:
-      case STRING:
-      case TIMESTAMP:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    double value = rowReader.read(row);
+    collector.putBoolean(time, value >= lower && upper >= value);
   }
 
   @Override
@@ -109,124 +82,21 @@ public class UDTFInRange implements UDTF {
     if (row.isNull(0)) {
       return null;
     }
-    switch (dataType) {
-      case INT32:
-        return row.getInt(0) >= lower && upper >= row.getInt(0);
-      case INT64:
-        return row.getLong(0) >= lower && upper >= row.getLong(0);
-      case FLOAT:
-        return row.getFloat(0) >= lower && upper >= row.getFloat(0);
-      case DOUBLE:
-        return row.getDouble(0) >= lower && upper >= row.getDouble(0);
-      case TIMESTAMP:
-      case BOOLEAN:
-      case DATE:
-      case STRING:
-      case TEXT:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    double value = rowReader.read(row);
+    return value >= lower && upper >= value;
   }
 
   @Override
   public void transform(Column[] columns, ColumnBuilder builder) throws Exception {
-    switch (dataType) {
-      case INT32:
-        transformInt(columns, builder);
-        return;
-      case INT64:
-        transformLong(columns, builder);
-        return;
-      case FLOAT:
-        transformFloat(columns, builder);
-        return;
-      case DOUBLE:
-        transformDouble(columns, builder);
-        return;
-      case BLOB:
-      case OBJECT:
-      case TEXT:
-      case DATE:
-      case STRING:
-      case BOOLEAN:
-      case TIMESTAMP:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
-  }
-
-  private void transformInt(Column[] columns, ColumnBuilder builder) {
-    int[] inputs = columns[0].getInts();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        boolean res = inputs[i] >= lower && upper >= inputs[i];
-        builder.writeBoolean(res);
-      }
-    }
-  }
-
-  private void transformLong(Column[] columns, ColumnBuilder builder) {
-    long[] inputs = columns[0].getLongs();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        boolean res = inputs[i] >= lower && upper >= inputs[i];
-        builder.writeBoolean(res);
-      }
-    }
-  }
-
-  private void transformFloat(Column[] columns, ColumnBuilder builder) {
-    float[] inputs = columns[0].getFloats();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        boolean res = inputs[i] >= lower && upper >= inputs[i];
-        builder.writeBoolean(res);
-      }
-    }
-  }
-
-  private void transformDouble(Column[] columns, ColumnBuilder builder) {
-    double[] inputs = columns[0].getDoubles();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
+    Column column = columns[0];
+    boolean[] isNulls = column.isNull();
+    int count = column.getPositionCount();
     for (int i = 0; i < count; i++) {
       if (isNulls[i]) {
         builder.appendNull();
       } else {
-        boolean res = inputs[i] >= lower && upper >= inputs[i];
-        builder.writeBoolean(res);
+        double value = columnReader.read(column, i);
+        builder.writeBoolean(value >= lower && upper >= value);
       }
     }
   }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java
index 947cbdb2c01..08179e24c54 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java
@@ -48,6 +48,8 @@ public abstract class UDTFMath implements UDTF {
   protected Transformer transformer;
 
   protected TSDataType dataType;
+  private TypeServices.NumericRowReader rowReader;
+  private TypeServices.NumericColumnReader columnReader;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -60,6 +62,10 @@ public abstract class UDTFMath implements UDTF {
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
     dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    org.apache.tsfile.read.common.type.Type type =
+        org.apache.tsfile.read.common.type.Type.fromTsDataType(dataType);
+    rowReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(type);
+    columnReader = TypeServices.NUMERIC_COLUMN_READER_SERVICE.call(type);
     configurations
         .setAccessStrategy(new MappableRowByRowAccessStrategy())
         .setOutputDataType(Type.DOUBLE);
@@ -71,37 +77,7 @@ public abstract class UDTFMath implements UDTF {
   @Override
   public void transform(Row row, PointCollector collector)
       throws UDFInputSeriesDataTypeNotValidException, IOException {
-    long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        collector.putDouble(time, transformer.transform(row.getInt(0)));
-        break;
-      case INT64:
-        collector.putDouble(time, transformer.transform(row.getLong(0)));
-        break;
-      case FLOAT:
-        collector.putDouble(time, transformer.transform(row.getFloat(0)));
-        break;
-      case DOUBLE:
-        collector.putDouble(time, transformer.transform(row.getDouble(0)));
-        break;
-      case BOOLEAN:
-      case TEXT:
-      case STRING:
-      case TIMESTAMP:
-      case DATE:
-      case BLOB:
-      case OBJECT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    collector.putDouble(row.getTime(), transformer.transform(rowReader.read(row)));
   }
 
   @Override
@@ -109,120 +85,19 @@ public abstract class UDTFMath implements UDTF {
     if (row.isNull(0)) {
       return null;
     }
-    switch (dataType) {
-      case INT32:
-        return transformer.transform(row.getInt(0));
-      case INT64:
-        return transformer.transform(row.getLong(0));
-      case FLOAT:
-        return transformer.transform(row.getFloat(0));
-      case DOUBLE:
-        return transformer.transform(row.getDouble(0));
-      case DATE:
-      case BLOB:
-      case OBJECT:
-      case STRING:
-      case TIMESTAMP:
-      case TEXT:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    return transformer.transform(rowReader.read(row));
   }
 
   @Override
   public void transform(Column[] columns, ColumnBuilder builder) throws Exception {
-    switch (dataType) {
-      case INT32:
-        transformInt(columns, builder);
-        return;
-      case INT64:
-        transformLong(columns, builder);
-        return;
-      case FLOAT:
-        transformFloat(columns, builder);
-        return;
-      case DOUBLE:
-        transformDouble(columns, builder);
-        return;
-      case TEXT:
-      case BOOLEAN:
-      case STRING:
-      case TIMESTAMP:
-      case BLOB:
-      case OBJECT:
-      case DATE:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
-  }
-
-  private void transformInt(Column[] columns, ColumnBuilder builder) {
-    int[] inputs = columns[0].getInts();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeDouble(transformer.transform(inputs[i]));
-      }
-    }
-  }
-
-  private void transformLong(Column[] columns, ColumnBuilder builder) {
-    long[] inputs = columns[0].getLongs();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeDouble(transformer.transform(inputs[i]));
-      }
-    }
-  }
-
-  private void transformFloat(Column[] columns, ColumnBuilder builder) {
-    float[] inputs = columns[0].getFloats();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeDouble(transformer.transform(inputs[i]));
-      }
-    }
-  }
-
-  private void transformDouble(Column[] columns, ColumnBuilder builder) {
-    double[] inputs = columns[0].getDoubles();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
+    Column column = columns[0];
+    boolean[] isNulls = column.isNull();
+    int count = column.getPositionCount();
     for (int i = 0; i < count; i++) {
       if (isNulls[i]) {
         builder.appendNull();
       } else {
-        builder.writeDouble(transformer.transform(inputs[i]));
+        builder.writeDouble(transformer.transform(columnReader.read(column, i)));
       }
     }
   }
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java
index b0c95bd0d23..2095d07172e 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java
@@ -29,12 +29,10 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
 import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
 import org.apache.iotdb.udf.api.customizer.strategy.MappableRowByRowAccessStrategy;
 import org.apache.iotdb.udf.api.exception.UDFException;
-import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
 import org.apache.iotdb.udf.api.type.Type;
 
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
-import org.apache.tsfile.enums.TSDataType;
 
 import java.io.IOException;
 
@@ -42,7 +40,8 @@ public class UDTFOnOff implements UDTF {
 
   protected double threshold;
 
-  private TSDataType dataType;
+  private TypeServices.NumericRowReader rowReader;
+  private TypeServices.NumericColumnReader columnReader;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
@@ -56,45 +55,19 @@ public class UDTFOnOff implements UDTF {
   public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
       throws MetadataException {
     threshold = parameters.getDouble("threshold");
-    dataType = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(0));
+    org.apache.tsfile.read.common.type.Type type =
+        UDFDataTypeTransformer.transformUDFDataTypeToReadType(parameters.getDataType(0));
+    rowReader = TypeServices.NUMERIC_ROW_READER_SERVICE.call(type);
+    columnReader = TypeServices.NUMERIC_COLUMN_READER_SERVICE.call(type);
     configurations
         .setAccessStrategy(new MappableRowByRowAccessStrategy())
         .setOutputDataType(Type.BOOLEAN);
   }
 
   @Override
-  public void transform(Row row, PointCollector collector)
-      throws UDFInputSeriesDataTypeNotValidException, IOException {
+  public void transform(Row row, PointCollector collector) throws IOException {
     long time = row.getTime();
-    switch (dataType) {
-      case INT32:
-        collector.putBoolean(time, row.getInt(0) >= threshold);
-        break;
-      case INT64:
-        collector.putBoolean(time, (row.getLong(0) >= threshold));
-        break;
-      case FLOAT:
-        collector.putBoolean(time, (row.getFloat(0) >= threshold));
-        break;
-      case DOUBLE:
-        collector.putBoolean(time, (row.getDouble(0) >= threshold));
-        break;
-      case DATE:
-      case BLOB:
-      case STRING:
-      case TIMESTAMP:
-      case TEXT:
-      case BOOLEAN:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    collector.putBoolean(time, rowReader.read(row) >= threshold);
   }
 
   @Override
@@ -102,119 +75,19 @@ public class UDTFOnOff implements UDTF {
     if (row.isNull(0)) {
       return null;
     }
-    switch (dataType) {
-      case INT32:
-        return row.getInt(0) >= threshold;
-      case INT64:
-        return row.getLong(0) >= threshold;
-      case FLOAT:
-        return row.getFloat(0) >= threshold;
-      case DOUBLE:
-        return row.getDouble(0) >= threshold;
-      case TEXT:
-      case BOOLEAN:
-      case STRING:
-      case TIMESTAMP:
-      case BLOB:
-      case DATE:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
+    return rowReader.read(row) >= threshold;
   }
 
   @Override
   public void transform(Column[] columns, ColumnBuilder builder) throws Exception {
-    switch (dataType) {
-      case INT32:
-        transformInt(columns, builder);
-        return;
-      case INT64:
-        transformLong(columns, builder);
-        return;
-      case FLOAT:
-        transformFloat(columns, builder);
-        return;
-      case DOUBLE:
-        transformDouble(columns, builder);
-        return;
-      case BLOB:
-      case OBJECT:
-      case DATE:
-      case STRING:
-      case TIMESTAMP:
-      case BOOLEAN:
-      case TEXT:
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            UDFDataTypeTransformer.transformToUDFDataType(dataType),
-            Type.INT32,
-            Type.INT64,
-            Type.FLOAT,
-            Type.DOUBLE);
-    }
-  }
-
-  private void transformInt(Column[] columns, ColumnBuilder builder) {
-    int[] inputs = columns[0].getInts();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeBoolean(inputs[i] >= threshold);
-      }
-    }
-  }
-
-  private void transformLong(Column[] columns, ColumnBuilder builder) {
-    long[] inputs = columns[0].getLongs();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeBoolean(inputs[i] >= threshold);
-      }
-    }
-  }
-
-  private void transformFloat(Column[] columns, ColumnBuilder builder) {
-    float[] inputs = columns[0].getFloats();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
-    for (int i = 0; i < count; i++) {
-      if (isNulls[i]) {
-        builder.appendNull();
-      } else {
-        builder.writeBoolean(inputs[i] >= threshold);
-      }
-    }
-  }
-
-  private void transformDouble(Column[] columns, ColumnBuilder builder) {
-    double[] inputs = columns[0].getDoubles();
-    boolean[] isNulls = columns[0].isNull();
-
-    int count = columns[0].getPositionCount();
+    Column column = columns[0];
+    boolean[] isNulls = column.isNull();
+    int count = column.getPositionCount();
     for (int i = 0; i < count; i++) {
       if (isNulls[i]) {
         builder.appendNull();
       } else {
-        builder.writeBoolean(inputs[i] >= threshold);
+        builder.writeBoolean(columnReader.read(column, i) >= threshold);
       }
     }
   }


(iotdb) 02/14: multiple refactors

Posted by ji...@apache.org.
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 85010f7ea887821dada25e1d5cc14038893e56e5
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 11:09:20 2026 +0800

    multiple refactors
---
 .../merge/comparator/JoinKeyComparatorFactory.java |  36 +-----
 .../calc/plan/planner/CommonOperatorUtils.java     | 129 ++-------------------
 .../org/apache/iotdb/calc/utils/TypeServices.java  |  40 +++++++
 .../apache/iotdb/calc/utils/TypeServicesTest.java  |  35 ++++++
 4 files changed, 89 insertions(+), 151 deletions(-)

diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/merge/comparator/JoinKeyComparatorFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/merge/comparator/JoinKeyComparatorFactory.java
index 841fe965c19..3b55c866911 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/merge/comparator/JoinKeyComparatorFactory.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/merge/comparator/JoinKeyComparatorFactory.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.calc.execution.operator.process.join.merge.comparator;
 
-import org.apache.iotdb.calc.i18n.CalcMessages;
+import org.apache.iotdb.calc.utils.TypeServices;
 
 import org.apache.tsfile.read.common.type.Type;
 
@@ -59,38 +59,6 @@ public class JoinKeyComparatorFactory {
   }
 
   public static JoinKeyComparator getComparator(Type type, boolean isAscending) {
-    switch (type.getTypeEnum()) {
-      case INT32:
-      case DATE:
-        return isAscending
-            ? AscIntTypeJoinKeyComparator.getInstance()
-            : DescIntTypeJoinKeyComparator.getInstance();
-      case INT64:
-      case TIMESTAMP:
-        return isAscending
-            ? AscLongTypeJoinKeyComparator.getInstance()
-            : DescLongTypeJoinKeyComparator.getInstance();
-      case FLOAT:
-        return isAscending
-            ? AscFloatTypeJoinKeyComparator.getInstance()
-            : DescFloatTypeJoinKeyComparator.getInstance();
-      case DOUBLE:
-        return isAscending
-            ? AscDoubleTypeJoinKeyComparator.getInstance()
-            : DescDoubleTypeJoinKeyComparator.getInstance();
-      case BOOLEAN:
-        return isAscending
-            ? AscBooleanTypeJoinKeyComparator.getInstance()
-            : DescBooleanTypeJoinKeyComparator.getInstance();
-      case STRING:
-      case BLOB:
-      case TEXT:
-        return isAscending
-            ? AscBinaryTypeJoinKeyComparator.getInstance()
-            : DescBinaryTypeJoinKeyComparator.getInstance();
-      default:
-        // other types are not supported.
-        throw new UnsupportedOperationException(CalcMessages.UNSUPPORTED_DATA_TYPE + type);
-    }
+    return TypeServices.JOIN_KEY_COMPARATOR_SERVICE.call(type).apply(isAscending);
   }
 }
diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/CommonOperatorUtils.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/CommonOperatorUtils.java
index a332122b257..463c22969ce 100644
--- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/CommonOperatorUtils.java
+++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/CommonOperatorUtils.java
@@ -28,33 +28,13 @@ import org.apache.iotdb.calc.execution.operator.process.fill.filter.MonthInterva
 import org.apache.iotdb.calc.execution.operator.process.fill.filter.MonthIntervalUSFillFilter;
 import org.apache.iotdb.calc.execution.operator.process.fill.identity.IdentityFill;
 import org.apache.iotdb.calc.execution.operator.process.fill.identity.IdentityLinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.linear.DoubleLinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.linear.FloatLinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.linear.IntLinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.linear.LongLinearFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.BinaryNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.BooleanNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.DoubleNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.FloatNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.IntNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.next.LongNextFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.BinaryPreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.BinaryPreviousFillWithTimeDuration;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.BooleanPreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.BooleanPreviousFillWithTimeDuration;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.DoublePreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.DoublePreviousFillWithTimeDuration;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.FloatPreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.FloatPreviousFillWithTimeDuration;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.IntPreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.IntPreviousFillWithTimeDuration;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.LongPreviousFill;
-import org.apache.iotdb.calc.execution.operator.process.fill.previous.LongPreviousFillWithTimeDuration;
 import org.apache.iotdb.calc.i18n.CalcMessages;
+import org.apache.iotdb.calc.utils.TypeServices;
 import org.apache.iotdb.commons.i18n.QueryMessages;
 
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.block.column.LongColumn;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.TimeDuration;
 
 import java.time.ZoneId;
@@ -77,31 +57,8 @@ public class CommonOperatorUtils {
   public static ILinearFill[] getLinearFill(int inputColumns, List<TSDataType> inputDataTypes) {
     ILinearFill[] linearFill = new ILinearFill[inputColumns];
     for (int i = 0; i < inputColumns; i++) {
-      switch (inputDataTypes.get(i)) {
-        case INT32:
-        case DATE:
-          linearFill[i] = new IntLinearFill();
-          break;
-        case INT64:
-        case TIMESTAMP:
-          linearFill[i] = new LongLinearFill();
-          break;
-        case FLOAT:
-          linearFill[i] = new FloatLinearFill();
-          break;
-        case DOUBLE:
-          linearFill[i] = new DoubleLinearFill();
-          break;
-        case BOOLEAN:
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          linearFill[i] = IDENTITY_LINEAR_FILL;
-          break;
-        default:
-          throw new IllegalArgumentException(UNKNOWN_DATATYPE + inputDataTypes.get(i));
-      }
+      linearFill[i] =
+          TypeServices.LINEAR_FILL_SERVICE.call(Type.fromTsDataType(inputDataTypes.get(i))).get();
     }
     return linearFill;
   }
@@ -115,49 +72,10 @@ public class CommonOperatorUtils {
 
     IFill[] previousFill = new IFill[inputColumns];
     for (int i = 0; i < inputColumns; i++) {
-      switch (inputDataTypes.get(i)) {
-        case BOOLEAN:
-          previousFill[i] =
-              filter == null
-                  ? new BooleanPreviousFill()
-                  : new BooleanPreviousFillWithTimeDuration(filter);
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          previousFill[i] =
-              filter == null
-                  ? new BinaryPreviousFill()
-                  : new BinaryPreviousFillWithTimeDuration(filter);
-          break;
-        case INT32:
-        case DATE:
-          previousFill[i] =
-              filter == null ? new IntPreviousFill() : new IntPreviousFillWithTimeDuration(filter);
-          break;
-        case INT64:
-        case TIMESTAMP:
-          previousFill[i] =
-              filter == null
-                  ? new LongPreviousFill()
-                  : new LongPreviousFillWithTimeDuration(filter);
-          break;
-        case FLOAT:
-          previousFill[i] =
-              filter == null
-                  ? new FloatPreviousFill()
-                  : new FloatPreviousFillWithTimeDuration(filter);
-          break;
-        case DOUBLE:
-          previousFill[i] =
-              filter == null
-                  ? new DoublePreviousFill()
-                  : new DoublePreviousFillWithTimeDuration(filter);
-          break;
-        default:
-          throw new IllegalArgumentException(UNKNOWN_DATATYPE + inputDataTypes.get(i));
-      }
+      previousFill[i] =
+          TypeServices.PREVIOUS_FILL_SERVICE
+              .call(Type.fromTsDataType(inputDataTypes.get(i)))
+              .apply(filter);
     }
     return previousFill;
   }
@@ -171,33 +89,10 @@ public class CommonOperatorUtils {
 
     ILinearFill[] nextFill = new ILinearFill[inputColumns];
     for (int i = 0; i < inputColumns; i++) {
-      switch (inputDataTypes.get(i)) {
-        case BOOLEAN:
-          nextFill[i] = new BooleanNextFill(filter);
-          break;
-        case TEXT:
-        case STRING:
-        case BLOB:
-        case OBJECT:
-          nextFill[i] = new BinaryNextFill(filter);
-          break;
-        case INT32:
-        case DATE:
-          nextFill[i] = new IntNextFill(filter);
-          break;
-        case INT64:
-        case TIMESTAMP:
-          nextFill[i] = new LongNextFill(filter);
-          break;
-        case FLOAT:
-          nextFill[i] = new FloatNextFill(filter);
-          break;
-        case DOUBLE:
-          nextFill[i] = new DoubleNextFill(filter);
-          break;
-        default:
-          throw new IllegalArgumentException(UNKNOWN_DATATYPE + inputDataTypes.get(i));
-      }
+      nextFill[i] =
+          TypeServices.NEXT_FILL_SERVICE
+              .call(Type.fromTsDataType(inputDataTypes.get(i)))
+              .apply(filter);
     }
     return nextFill;
   }
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 23ab14974df..705c1b02aa4 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
@@ -19,6 +19,45 @@
 
 package org.apache.iotdb.calc.utils;
 
+import org.apache.iotdb.calc.execution.operator.process.fill.IFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.IFillFilter;
+import org.apache.iotdb.calc.execution.operator.process.fill.ILinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.identity.IdentityLinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.linear.DoubleLinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.linear.FloatLinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.linear.IntLinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.linear.LongLinearFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.BinaryNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.BooleanNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.DoubleNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.FloatNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.IntNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.next.LongNextFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.BinaryPreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.BinaryPreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.BooleanPreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.BooleanPreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.DoublePreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.DoublePreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.FloatPreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.FloatPreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.IntPreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.IntPreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.LongPreviousFill;
+import org.apache.iotdb.calc.execution.operator.process.fill.previous.LongPreviousFillWithTimeDuration;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscBinaryTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscBooleanTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscDoubleTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscFloatTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscIntTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscLongTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescBinaryTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescBooleanTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescDoubleTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescFloatTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescIntTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.DescLongTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator;
 import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition;
 import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
 import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.GroupedMaxMinByBaseAccumulator;
@@ -34,6 +73,7 @@ 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.Type;
 import org.apache.tsfile.read.common.type.service.TypeService;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.DateUtils;
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
index f11222c26fe..6f5ac8a9b01 100644
--- 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
@@ -18,6 +18,9 @@
 
 package org.apache.iotdb.calc.utils;
 
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.AscIntTypeJoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator;
+import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparatorFactory;
 import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition;
 import org.apache.iotdb.calc.execution.operator.process.window.utils.ColumnList;
 
@@ -38,6 +41,7 @@ import java.util.Optional;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -94,6 +98,37 @@ public class TypeServicesTest {
                 .apply(0));
   }
 
+  @Test
+  public void testFillServicesUseTypeStrategies() {
+    assertNotNull(
+        TypeServices.LINEAR_FILL_SERVICE.call(Type.fromTsDataType(TSDataType.INT32)).get());
+    assertNotNull(
+        TypeServices.PREVIOUS_FILL_SERVICE.call(Type.fromTsDataType(TSDataType.TEXT)).apply(null));
+    assertNotNull(
+        TypeServices.NEXT_FILL_SERVICE.call(Type.fromTsDataType(TSDataType.DOUBLE)).apply(null));
+
+    Assert.assertThrows(
+        IllegalArgumentException.class,
+        () -> TypeServices.LINEAR_FILL_SERVICE.call(Type.fromTsDataType(TSDataType.UNKNOWN)).get());
+    Assert.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            TypeServices.PREVIOUS_FILL_SERVICE
+                .call(Type.fromTsDataType(TSDataType.VECTOR))
+                .apply(null));
+  }
+
+  @Test
+  public void testJoinKeyComparatorServiceUsesTypeStrategies() {
+    JoinKeyComparator comparator =
+        JoinKeyComparatorFactory.getComparator(Type.fromTsDataType(TSDataType.INT32), true);
+    assertSame(AscIntTypeJoinKeyComparator.getInstance(), comparator);
+
+    Assert.assertThrows(
+        UnsupportedOperationException.class,
+        () -> JoinKeyComparatorFactory.getComparator(Type.fromTsDataType(TSDataType.VECTOR), true));
+  }
+
   // Covers every supported RANGE-frame type and guards native integer overflow and long precision.
   @Test
   public void testRangeFrameComparatorPreservesNativeArithmetic() {


(iotdb) 07/14: multiple refactors

Posted by ji...@apache.org.
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 954731b1a5132c714c26656bb0042c730d53b6b3
Author: Tian Jiang <jt...@163.com>
AuthorDate: Wed Aug 26 17:51:37 2026 +0800

    multiple refactors
---
 .../org/apache/iotdb/rpc/IoTDBJDBCDataSet.java     | 177 +++++----------------
 .../java/org/apache/iotdb/rpc/TypeServices.java    |  68 ++++++++
 .../org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java | 106 ++++++++++++
 3 files changed, 216 insertions(+), 135 deletions(-)

diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java
index 000ab5a2799..a578f6e4a81 100644
--- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java
@@ -29,14 +29,12 @@ import org.apache.iotdb.service.rpc.thrift.TSQueryDataSet;
 
 import org.apache.thrift.TException;
 import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.read.common.type.Type;
 import org.apache.tsfile.utils.BytesUtils;
-import org.apache.tsfile.utils.DateUtils;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
 
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.sql.Timestamp;
 import java.time.ZoneId;
 import java.util.ArrayList;
@@ -156,38 +154,7 @@ public class IoTDBJDBCDataSet {
 
     time = new byte[Long.BYTES];
     currentBitmap = new byte[columnTypeDeduplicatedList.size()];
-    values = new byte[columnTypeDeduplicatedList.size()][];
-    for (int i = 0; i < values.length; i++) {
-      TSDataType dataType = columnTypeDeduplicatedList.get(i);
-      switch (dataType) {
-        case BOOLEAN:
-          values[i] = new byte[1];
-          break;
-        case INT32:
-        case DATE:
-          values[i] = new byte[Integer.BYTES];
-          break;
-        case INT64:
-        case TIMESTAMP:
-          values[i] = new byte[Long.BYTES];
-          break;
-        case FLOAT:
-          values[i] = new byte[Float.BYTES];
-          break;
-        case DOUBLE:
-          values[i] = new byte[Double.BYTES];
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          values[i] = null;
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(DATA_TYPE_NOT_SUPPORTED, columnTypeDeduplicatedList.get(i)));
-      }
-    }
+    values = initializeValueBuffers(columnTypeDeduplicatedList);
     this.tsQueryDataSet = queryDataSet;
     this.emptyResultSet = (queryDataSet == null || !queryDataSet.time.hasRemaining());
   }
@@ -275,38 +242,7 @@ public class IoTDBJDBCDataSet {
 
     time = new byte[Long.BYTES];
     currentBitmap = new byte[columnTypeDeduplicatedList.size()];
-    values = new byte[columnTypeDeduplicatedList.size()][];
-    for (int i = 0; i < values.length; i++) {
-      TSDataType dataType = columnTypeDeduplicatedList.get(i);
-      switch (dataType) {
-        case BOOLEAN:
-          values[i] = new byte[1];
-          break;
-        case INT32:
-        case DATE:
-          values[i] = new byte[Integer.BYTES];
-          break;
-        case INT64:
-        case TIMESTAMP:
-          values[i] = new byte[Long.BYTES];
-          break;
-        case FLOAT:
-          values[i] = new byte[Float.BYTES];
-          break;
-        case DOUBLE:
-          values[i] = new byte[Double.BYTES];
-          break;
-        case TEXT:
-        case BLOB:
-        case STRING:
-        case OBJECT:
-          values[i] = null;
-          break;
-        default:
-          throw new UnSupportedDataTypeException(
-              String.format(DATA_TYPE_NOT_SUPPORTED, columnTypeDeduplicatedList.get(i)));
-      }
-    }
+    values = initializeValueBuffers(columnTypeDeduplicatedList);
     this.tsQueryDataSet = queryDataSet;
     this.emptyResultSet = (queryDataSet == null || !queryDataSet.time.hasRemaining());
   }
@@ -420,26 +356,11 @@ public class IoTDBJDBCDataSet {
       if (!isNull(i, rowsIndex)) {
         ByteBuffer valueBuffer = tsQueryDataSet.valueList.get(i);
         TSDataType dataType = columnTypeDeduplicatedList.get(i);
-        switch (dataType) {
-          case BOOLEAN:
-          case INT32:
-          case INT64:
-          case FLOAT:
-          case DOUBLE:
-          case DATE:
-          case TIMESTAMP:
-            valueBuffer.get(values[i]);
-            break;
-          case TEXT:
-          case BLOB:
-          case STRING:
-          case OBJECT:
-            int length = valueBuffer.getInt();
-            values[i] = ReadWriteIOUtils.readBytes(valueBuffer, length);
-            break;
-          default:
-            throw new UnSupportedDataTypeException(
-                String.format(DATA_TYPE_NOT_SUPPORTED, columnTypeDeduplicatedList.get(i)));
+        if (dataType.isBinary()) {
+          int length = valueBuffer.getInt();
+          values[i] = ReadWriteIOUtils.readBytes(valueBuffer, length);
+        } else {
+          valueBuffer.get(values[i]);
         }
       }
     }
@@ -447,6 +368,34 @@ public class IoTDBJDBCDataSet {
     hasCachedRecord = true;
   }
 
+  static byte[][] initializeValueBuffers(List<TSDataType> dataTypes) {
+    byte[][] valueBuffers = new byte[dataTypes.size()][];
+    for (int i = 0; i < dataTypes.size(); i++) {
+      TSDataType dataType = dataTypes.get(i);
+      if (dataType == TSDataType.VECTOR || dataType == TSDataType.UNKNOWN) {
+        throw unsupportedDataType(dataType);
+      }
+      if (dataType.isBinary()) {
+        continue;
+      }
+      final int dataTypeSize;
+      try {
+        dataTypeSize = dataType.getDataTypeSize();
+      } catch (UnSupportedDataTypeException e) {
+        throw unsupportedDataType(dataType);
+      }
+      if (dataTypeSize == 0) {
+        throw unsupportedDataType(dataType);
+      }
+      valueBuffers[i] = new byte[dataTypeSize];
+    }
+    return valueBuffers;
+  }
+
+  private static UnSupportedDataTypeException unsupportedDataType(TSDataType dataType) {
+    return new UnSupportedDataTypeException(String.format(DATA_TYPE_NOT_SUPPORTED, dataType));
+  }
+
   public boolean isNull(int columnIndex) throws StatementExecutionException {
     int index = columnOrdinalMap.get(findColumnNameByIndex(columnIndex)) - START_INDEX;
     // time column will never be null
@@ -597,30 +546,9 @@ public class IoTDBJDBCDataSet {
   }
 
   public String getString(int index, TSDataType tsDataType, byte[][] values) {
-    switch (tsDataType) {
-      case BOOLEAN:
-        return String.valueOf(BytesUtils.bytesToBool(values[index]));
-      case INT32:
-        return String.valueOf(BytesUtils.bytesToInt(values[index]));
-      case INT64:
-      case TIMESTAMP:
-        return String.valueOf(BytesUtils.bytesToLong(values[index]));
-      case FLOAT:
-        return String.valueOf(BytesUtils.bytesToFloat(values[index]));
-      case DOUBLE:
-        return String.valueOf(BytesUtils.bytesToDouble(values[index]));
-      case TEXT:
-      case STRING:
-        return new String(values[index], StandardCharsets.UTF_8);
-      case OBJECT:
-        return BytesUtils.parseObjectByteArrayToString(values[index]);
-      case BLOB:
-        return BytesUtils.parseBlobByteArrayToString(values[index]);
-      case DATE:
-        return DateUtils.formatDate(BytesUtils.bytesToInt(values[index]));
-      default:
-        return null;
-    }
+    return TypeServices.JDBC_STRING_READER_SERVICE
+        .call(Type.fromTsDataType(tsDataType))
+        .apply(values[index]);
   }
 
   public Object getObjectByName(String columnName) throws StatementExecutionException {
@@ -638,30 +566,9 @@ public class IoTDBJDBCDataSet {
   }
 
   public Object getObject(int index, TSDataType tsDataType, byte[][] values) {
-    switch (tsDataType) {
-      case BOOLEAN:
-        return BytesUtils.bytesToBool(values[index]);
-      case INT32:
-        return BytesUtils.bytesToInt(values[index]);
-      case INT64:
-        return BytesUtils.bytesToLong(values[index]);
-      case FLOAT:
-        return BytesUtils.bytesToFloat(values[index]);
-      case DOUBLE:
-        return BytesUtils.bytesToDouble(values[index]);
-      case TEXT:
-      case STRING:
-        return new String(values[index], StandardCharsets.UTF_8);
-      case OBJECT:
-      case BLOB:
-        return new Binary(values[index]);
-      case TIMESTAMP:
-        return new Timestamp(BytesUtils.bytesToLong(values[index]));
-      case DATE:
-        return DateUtils.parseIntToDate(BytesUtils.bytesToInt(values[index]));
-      default:
-        return null;
-    }
+    return TypeServices.JDBC_OBJECT_READER_SERVICE
+        .call(Type.fromTsDataType(tsDataType))
+        .apply(values[index]);
   }
 
   public String findColumnNameByIndex(int columnIndex) throws StatementExecutionException {
diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
new file mode 100644
index 00000000000..4e1cec95c6f
--- /dev/null
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TypeServices.java
@@ -0,0 +1,68 @@
+/*
+ * 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.rpc;
+
+import org.apache.tsfile.read.common.type.service.TypeService;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.BytesUtils;
+import org.apache.tsfile.utils.DateUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.Timestamp;
+import java.util.function.Function;
+
+final class TypeServices {
+
+  static final TypeService<Function<byte[], String>> JDBC_STRING_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> value -> String.valueOf(BytesUtils.bytesToBool(value));
+            case INT32 -> value -> String.valueOf(BytesUtils.bytesToInt(value));
+            case INT64, TIMESTAMP -> value -> String.valueOf(BytesUtils.bytesToLong(value));
+            case FLOAT -> value -> String.valueOf(BytesUtils.bytesToFloat(value));
+            case DOUBLE -> value -> String.valueOf(BytesUtils.bytesToDouble(value));
+            case TEXT, STRING -> value -> new String(value, StandardCharsets.UTF_8);
+            case OBJECT -> BytesUtils::parseObjectByteArrayToString;
+            case BLOB -> BytesUtils::parseBlobByteArrayToString;
+            case DATE -> value -> DateUtils.formatDate(BytesUtils.bytesToInt(value));
+            case ROW, UNKNOWN, VECTOR -> ignored -> null;
+          };
+
+  static final TypeService<Function<byte[], Object>> JDBC_OBJECT_READER_SERVICE =
+      type ->
+          switch (type.getTypeEnum()) {
+            case BOOLEAN -> BytesUtils::bytesToBool;
+            case INT32 -> BytesUtils::bytesToInt;
+            case INT64 -> BytesUtils::bytesToLong;
+            case FLOAT -> BytesUtils::bytesToFloat;
+            case DOUBLE -> BytesUtils::bytesToDouble;
+            case TEXT, STRING -> value -> new String(value, StandardCharsets.UTF_8);
+            case OBJECT, BLOB -> Binary::new;
+            case TIMESTAMP -> value -> new Timestamp(BytesUtils.bytesToLong(value));
+            case DATE -> value -> DateUtils.parseIntToDate(BytesUtils.bytesToInt(value));
+            case ROW, UNKNOWN, VECTOR -> ignored -> null;
+          };
+
+  static {
+    JDBC_STRING_READER_SERVICE.check();
+    JDBC_OBJECT_READER_SERVICE.check();
+  }
+
+  private TypeServices() {}
+}
diff --git a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
new file mode 100644
index 00000000000..af98f2a60ec
--- /dev/null
+++ b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/IoTDBJDBCDataSetTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.rpc;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.read.common.type.UnknownType;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.BytesUtils;
+import org.apache.tsfile.write.UnSupportedDataTypeException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class IoTDBJDBCDataSetTest {
+
+  @Test
+  public void testInitializeValueBuffersUsesDataTypeInterfaces() {
+    byte[][] buffers =
+        IoTDBJDBCDataSet.initializeValueBuffers(
+            Arrays.asList(
+                TSDataType.BOOLEAN,
+                TSDataType.INT32,
+                TSDataType.INT64,
+                TSDataType.FLOAT,
+                TSDataType.DOUBLE,
+                TSDataType.DATE,
+                TSDataType.TIMESTAMP,
+                TSDataType.TEXT,
+                TSDataType.STRING,
+                TSDataType.BLOB,
+                TSDataType.OBJECT));
+
+    Assert.assertEquals(Byte.BYTES, buffers[0].length);
+    Assert.assertEquals(Integer.BYTES, buffers[1].length);
+    Assert.assertEquals(Long.BYTES, buffers[2].length);
+    Assert.assertEquals(Float.BYTES, buffers[3].length);
+    Assert.assertEquals(Double.BYTES, buffers[4].length);
+    Assert.assertEquals(Integer.BYTES, buffers[5].length);
+    Assert.assertEquals(Long.BYTES, buffers[6].length);
+    Assert.assertNull(buffers[7]);
+    Assert.assertNull(buffers[8]);
+    Assert.assertNull(buffers[9]);
+    Assert.assertNull(buffers[10]);
+  }
+
+  @Test
+  public void testInitializeValueBuffersRejectsInternalTypes() {
+    assertUnsupported(TSDataType.UNKNOWN);
+    assertUnsupported(TSDataType.VECTOR);
+  }
+
+  @Test
+  public void testJdbcValueReadersUseTypeServices() {
+    Assert.assertEquals(
+        "42",
+        TypeServices.JDBC_STRING_READER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.INT32))
+            .apply(BytesUtils.intToBytes(42)));
+    Assert.assertEquals(
+        42,
+        TypeServices.JDBC_OBJECT_READER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.INT32))
+            .apply(BytesUtils.intToBytes(42)));
+
+    byte[] binary = new byte[] {1, 2, 3};
+    Assert.assertEquals(
+        new Binary(binary),
+        TypeServices.JDBC_OBJECT_READER_SERVICE
+            .call(Type.fromTsDataType(TSDataType.BLOB))
+            .apply(binary));
+
+    Assert.assertNull(
+        TypeServices.JDBC_STRING_READER_SERVICE.call(UnknownType.UNKNOWN).apply(new byte[0]));
+    Assert.assertNull(
+        TypeServices.JDBC_OBJECT_READER_SERVICE.call(UnknownType.UNKNOWN).apply(new byte[0]));
+  }
+
+  private static void assertUnsupported(TSDataType dataType) {
+    UnSupportedDataTypeException exception =
+        Assert.assertThrows(
+            UnSupportedDataTypeException.class,
+            () -> IoTDBJDBCDataSet.initializeValueBuffers(Arrays.asList(dataType)));
+    Assert.assertTrue(
+        exception
+            .getMessage()
+            .endsWith(String.format(IoTDBJDBCDataSet.DATA_TYPE_NOT_SUPPORTED, dataType)));
+  }
+}