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

jackietien pushed a commit to branch ty/TableModelGrammar
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/ty/TableModelGrammar by this 
push:
     new e31904b5c7b add support for diff, cast, substring and round
e31904b5c7b is described below

commit e31904b5c7b26f588b20576728184a71b3f275e3
Author: JackieTien97 <[email protected]>
AuthorDate: Tue Apr 23 16:37:28 2024 +0800

    add support for diff, cast, substring and round
---
 .../protocol/thrift/impl/ClientRPCServiceImpl.java |   4 +-
 .../relational/ColumnTransformerBuilder.java       | 512 ++++++++++++++-------
 .../plan/planner/LocalExecutionPlanner.java        |   6 +-
 .../plan/planner/TableOperatorGenerator.java       |  13 +-
 .../ConvertPredicateToTimeFilterVisitor.java       |   3 +-
 .../relational/metadata/TableMetadataImpl.java     |  51 +-
 .../ternary/CompareTernaryColumnTransformer.java   |  24 -
 .../column/ternary/TernaryColumnTransformer.java   |  23 +
 .../column/unary/scalar/DiffColumnTransformer.java |  73 +++
 .../unary/scalar/Replace2ColumnTransformer.java    |  66 +++
 .../scalar/Replace3ColumnTransformer.java}         |  71 +--
 .../unary/scalar/RoundColumnTransformer.java       |  78 ++++
 .../unary/scalar/SubString2ColumnTransformer.java  |  63 +++
 .../unary/scalar/SubString3ColumnTransformer.java  |  78 ++++
 14 files changed, 830 insertions(+), 235 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
index 4e8d16ba9bb..aaf2fa1a514 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
@@ -76,13 +76,13 @@ import 
org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult;
 import org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution;
 import org.apache.iotdb.db.queryengine.plan.parser.ASTVisitor;
 import org.apache.iotdb.db.queryengine.plan.parser.StatementGenerator;
+import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationStep;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimeParameter;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.InputLocation;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.SeriesScanOptions;
 import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
-import 
org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadataImpl;
 import org.apache.iotdb.db.queryengine.plan.statement.Statement;
 import org.apache.iotdb.db.queryengine.plan.statement.StatementType;
 import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
@@ -269,7 +269,7 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
   public ClientRPCServiceImpl() {
     partitionFetcher = ClusterPartitionFetcher.getInstance();
     schemaFetcher = ClusterSchemaFetcher.getInstance();
-    metadata = new TableMetadataImpl();
+    metadata = LocalExecutionPlanner.getInstance().metadata;
     relationSqlParser = new SqlParser();
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
index 577ef24121c..2d4ce5ac0e6 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
@@ -19,10 +19,14 @@
 
 package org.apache.iotdb.db.queryengine.execution.relational;
 
+import org.apache.iotdb.commons.udf.builtin.BuiltinScalarFunction;
+import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.queryengine.common.SessionInfo;
 import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.InputLocation;
+import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
+import 
org.apache.iotdb.db.queryengine.plan.relational.type.TypeNotFoundException;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.ArithmeticAdditionColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.ArithmeticDivisionColumnTransformer;
@@ -42,11 +46,23 @@ import 
org.apache.iotdb.db.queryengine.transformation.dag.column.leaf.NullColumn
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.leaf.TimeColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.multi.LogicalAndMultiColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.multi.LogicalOrMultiColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ternary.BetweenColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.ArithmeticNegationColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.InColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.IsNullColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.LogicNotColumnTransformer;
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.RegularColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.CastFunctionColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.DiffColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.DiffFunctionColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.Replace2ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.Replace3ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.ReplaceFunctionColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.RoundColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.RoundFunctionColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.SubString2ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.SubString3ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.SubStringFunctionColumnTransformer;
 import org.apache.iotdb.db.relational.sql.tree.ArithmeticBinaryExpression;
 import org.apache.iotdb.db.relational.sql.tree.ArithmeticUnaryExpression;
 import org.apache.iotdb.db.relational.sql.tree.AstVisitor;
@@ -97,6 +113,9 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoIndexScanChecker.isStringLiteral;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature;
 import static org.apache.tsfile.read.common.type.BinaryType.TEXT;
 import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN;
 import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE;
@@ -118,40 +137,45 @@ public class ColumnTransformerBuilder
   @Override
   protected ColumnTransformer visitArithmeticBinary(
       ArithmeticBinaryExpression node, Context context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
-            node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        DOUBLE, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.DOUBLE);
-                return identity;
-              } else {
-                ColumnTransformer left = process(node.getLeft(), context);
-                ColumnTransformer right = process(node.getRight(), context);
-                switch (node.getOperator()) {
-                  case ADD:
-                    return new ArithmeticAdditionColumnTransformer(DOUBLE, 
left, right);
-                  case SUBTRACT:
-                    return new ArithmeticSubtractionColumnTransformer(DOUBLE, 
left, right);
-                  case MULTIPLY:
-                    return new 
ArithmeticMultiplicationColumnTransformer(DOUBLE, left, right);
-                  case DIVIDE:
-                    return new ArithmeticDivisionColumnTransformer(DOUBLE, 
left, right);
-                  case MODULUS:
-                    return new ArithmeticModuloColumnTransformer(DOUBLE, left, 
right);
-                  default:
-                    throw new UnsupportedOperationException(
-                        String.format(UNSUPPORTED_EXPRESSION, 
node.getOperator()));
-                }
-              }
-            });
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                DOUBLE, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.DOUBLE);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer left = process(node.getLeft(), context);
+        ColumnTransformer right = process(node.getRight(), context);
+        ColumnTransformer child;
+        switch (node.getOperator()) {
+          case ADD:
+            child = new ArithmeticAdditionColumnTransformer(DOUBLE, left, 
right);
+            break;
+          case SUBTRACT:
+            child = new ArithmeticSubtractionColumnTransformer(DOUBLE, left, 
right);
+            break;
+          case MULTIPLY:
+            child = new ArithmeticMultiplicationColumnTransformer(DOUBLE, 
left, right);
+            break;
+          case DIVIDE:
+            child = new ArithmeticDivisionColumnTransformer(DOUBLE, left, 
right);
+            break;
+          case MODULUS:
+            child = new ArithmeticModuloColumnTransformer(DOUBLE, left, right);
+            break;
+          default:
+            throw new UnsupportedOperationException(
+                String.format(UNSUPPORTED_EXPRESSION, node.getOperator()));
+        }
+        context.cache.put(node, child);
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
@@ -163,25 +187,24 @@ public class ColumnTransformerBuilder
       case PLUS:
         return process(node.getValue(), context);
       case MINUS:
-        ColumnTransformer res =
-            context.cache.computeIfAbsent(
-                node,
-                n -> {
-                  if (context.hasSeen.containsKey(node)) {
-                    IdentityColumnTransformer identity =
-                        new IdentityColumnTransformer(
-                            DOUBLE, context.originSize + 
context.commonTransformerList.size());
-                    ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                    columnTransformer.addReferenceCount();
-                    context.commonTransformerList.add(columnTransformer);
-                    context.leafList.add(identity);
-                    context.inputDataTypes.add(TSDataType.DOUBLE);
-                    return identity;
-                  } else {
-                    ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                    return new ArithmeticNegationColumnTransformer(DOUBLE, 
childColumnTransformer);
-                  }
-                });
+        if (!context.cache.containsKey(node)) {
+          if (context.hasSeen.containsKey(node)) {
+            IdentityColumnTransformer identity =
+                new IdentityColumnTransformer(
+                    DOUBLE, context.originSize + 
context.commonTransformerList.size());
+            ColumnTransformer columnTransformer = context.hasSeen.get(node);
+            columnTransformer.addReferenceCount();
+            context.commonTransformerList.add(columnTransformer);
+            context.leafList.add(identity);
+            context.inputDataTypes.add(TSDataType.DOUBLE);
+            context.cache.put(node, identity);
+          } else {
+            ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
+            context.cache.put(
+                node, new ArithmeticNegationColumnTransformer(DOUBLE, 
childColumnTransformer));
+          }
+        }
+        ColumnTransformer res = context.cache.get(node);
         res.addReferenceCount();
         return res;
       default:
@@ -191,12 +214,58 @@ public class ColumnTransformerBuilder
 
   @Override
   protected ColumnTransformer visitBetweenPredicate(BetweenPredicate node, 
Context context) {
-    throw new 
UnsupportedOperationException(String.format(UNSUPPORTED_EXPRESSION, node));
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer value = this.process(node.getValue(), context);
+        ColumnTransformer min = this.process(node.getMin(), context);
+        ColumnTransformer max = this.process(node.getMax(), context);
+        context.cache.put(node, new BetweenColumnTransformer(BOOLEAN, value, 
min, max, false));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
+    res.addReferenceCount();
+    return res;
   }
 
   @Override
   protected ColumnTransformer visitCast(Cast node, Context context) {
-    throw new 
UnsupportedOperationException(String.format(UNSUPPORTED_EXPRESSION, node));
+
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                columnTransformer.getType(),
+                context.originSize + context.commonTransformerList.size());
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(getTSDataType(columnTransformer.getType()));
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer child = this.process(node.getExpression(), context);
+        Type type;
+        try {
+          type = context.metadata.getType(toTypeSignature(node.getType()));
+        } catch (TypeNotFoundException e) {
+          throw new SemanticException(String.format("Unknown type: %s", 
node.getType()));
+        }
+        context.cache.put(node, new CastFunctionColumnTransformer(type, 
child));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
+    res.addReferenceCount();
+    return res;
   }
 
   @Override
@@ -297,7 +366,6 @@ public class ColumnTransformerBuilder
   @Override
   protected ColumnTransformer visitComparisonExpression(
       ComparisonExpression node, Context context) {
-    // fixme why using computeIfAbsent throw npe
     ColumnTransformer comparisonTransformer;
     if (context.cache.containsKey(node)) {
       comparisonTransformer = context.cache.get(node);
@@ -405,141 +473,247 @@ public class ColumnTransformerBuilder
 
   @Override
   protected ColumnTransformer visitFunctionCall(FunctionCall node, Context 
context) {
-    throw new 
UnsupportedOperationException(String.format(UNSUPPORTED_EXPRESSION, node));
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                columnTransformer.getType(),
+                context.originSize + context.commonTransformerList.size());
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(getTSDataType(columnTransformer.getType()));
+        context.cache.put(node, identity);
+      } else {
+        context.cache.put(
+            node,
+            getFunctionColumnTransformer(node.getName().getSuffix(), 
node.getArguments(), context));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
+    res.addReferenceCount();
+    return res;
+  }
+
+  private ColumnTransformer getFunctionColumnTransformer(
+      String functionName, List<Expression> children, Context context) {
+    // builtin scalar function
+    if 
(BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName)) {
+      boolean ignoreNull = true;
+      if (children.size() > 1) {
+        if (isBooleanLiteral(children.get(1))) {
+          ignoreNull = ((BooleanLiteral) children.get(1)).getValue();
+        } else {
+          return new DiffColumnTransformer(
+              DOUBLE,
+              this.process(children.get(0), context),
+              this.process(children.get(1), context));
+        }
+      }
+      return new DiffFunctionColumnTransformer(
+          DOUBLE, this.process(children.get(0), context), ignoreNull);
+    } else if 
(BuiltinScalarFunction.ROUND.getFunctionName().equalsIgnoreCase(functionName)) {
+      int places = 0;
+      if (children.size() > 1) {
+        if (isLongLiteral(children.get(1))) {
+          places = (int) ((LongLiteral) children.get(1)).getParsedValue();
+        } else {
+          return new RoundColumnTransformer(
+              DOUBLE,
+              this.process(children.get(0), context),
+              this.process(children.get(1), context));
+        }
+      }
+      return new RoundFunctionColumnTransformer(
+          DOUBLE, this.process(children.get(0), context), places);
+    } else if 
(BuiltinScalarFunction.REPLACE.getFunctionName().equalsIgnoreCase(functionName))
 {
+      if (children.size() == 2) {
+        if (isStringLiteral(children.get(1))) {
+          return new ReplaceFunctionColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              ((StringLiteral) children.get(1)).getValue(),
+              "");
+        } else {
+          return new Replace2ColumnTransformer(
+              TEXT, this.process(children.get(0), context), 
this.process(children.get(1), context));
+        }
+      } else {
+        // size == 3
+        if (isStringLiteral(children.get(1)) && 
isStringLiteral(children.get(2))) {
+          return new ReplaceFunctionColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              ((StringLiteral) children.get(1)).getValue(),
+              ((StringLiteral) children.get(2)).getValue());
+        } else {
+          return new Replace3ColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              this.process(children.get(1), context),
+              this.process(children.get(2), context));
+        }
+      }
+    } else if 
(BuiltinScalarFunction.SUBSTRING.getFunctionName().equalsIgnoreCase(functionName))
 {
+      if (children.size() == 2) {
+        if (isLongLiteral(children.get(1))) {
+          return new SubStringFunctionColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              (int) ((LongLiteral) children.get(1)).getParsedValue(),
+              Integer.MAX_VALUE);
+        } else {
+          return new SubString2ColumnTransformer(
+              TEXT, this.process(children.get(0), context), 
this.process(children.get(1), context));
+        }
+      } else {
+        // size == 3
+        if (isLongLiteral(children.get(1)) && isLongLiteral(children.get(2))) {
+          return new SubStringFunctionColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              (int) ((LongLiteral) children.get(1)).getParsedValue(),
+              (int) ((LongLiteral) children.get(2)).getParsedValue());
+        } else {
+          return new SubString3ColumnTransformer(
+              TEXT,
+              this.process(children.get(0), context),
+              this.process(children.get(1), context),
+              this.process(children.get(2), context));
+        }
+      }
+    }
+
+    throw new IllegalArgumentException(String.format("Unknown function: %s", 
functionName));
   }
 
   @Override
   protected ColumnTransformer visitInPredicate(InPredicate node, Context 
context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
-            node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        BOOLEAN, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.BOOLEAN);
-                return identity;
-              } else {
-                ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                InListExpression inListExpression = (InListExpression) 
node.getValueList();
-                List<Expression> expressionList = inListExpression.getValues();
-                List<Literal> values = new ArrayList<>();
-                for (Expression expression : expressionList) {
-                  checkArgument(expression instanceof Literal);
-                  values.add((Literal) expression);
-                }
-                return new InColumnTransformer(BOOLEAN, 
childColumnTransformer, values);
-              }
-            });
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer childColumnTransformer = process(node.getValue(), 
context);
+        InListExpression inListExpression = (InListExpression) 
node.getValueList();
+        List<Expression> expressionList = inListExpression.getValues();
+        List<Literal> values = new ArrayList<>();
+        for (Expression expression : expressionList) {
+          checkArgument(expression instanceof Literal);
+          values.add((Literal) expression);
+        }
+        context.cache.put(node, new InColumnTransformer(BOOLEAN, 
childColumnTransformer, values));
+      }
+    }
+
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
 
   @Override
   protected ColumnTransformer visitNotExpression(NotExpression node, Context 
context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
-            node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        BOOLEAN, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.BOOLEAN);
-                return identity;
-              } else {
-                ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                return new LogicNotColumnTransformer(BOOLEAN, 
childColumnTransformer);
-              }
-            });
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer childColumnTransformer = process(node.getValue(), 
context);
+        context.cache.put(node, new LogicNotColumnTransformer(BOOLEAN, 
childColumnTransformer));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
 
   @Override
   protected ColumnTransformer visitLikePredicate(LikePredicate node, Context 
context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer childColumnTransformer = process(node.getValue(), 
context);
+        context.cache.put(
             node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        BOOLEAN, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.BOOLEAN);
-                return identity;
-              } else {
-                ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                return new RegularColumnTransformer(
-                    BOOLEAN,
-                    childColumnTransformer,
-                    compileRegex(
-                        parseLikePatternToRegex(((StringLiteral) 
node.getPattern()).getValue())));
-              }
-            });
+            new RegularColumnTransformer(
+                BOOLEAN,
+                childColumnTransformer,
+                compileRegex(
+                    parseLikePatternToRegex(((StringLiteral) 
node.getPattern()).getValue()))));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
 
   @Override
   protected ColumnTransformer visitIsNotNullPredicate(IsNotNullPredicate node, 
Context context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
-            node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        BOOLEAN, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.BOOLEAN);
-                return identity;
-              } else {
-                ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                return new IsNullColumnTransformer(BOOLEAN, 
childColumnTransformer, true);
-              }
-            });
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer childColumnTransformer = process(node.getValue(), 
context);
+        context.cache.put(node, new IsNullColumnTransformer(BOOLEAN, 
childColumnTransformer, true));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
 
   @Override
   protected ColumnTransformer visitIsNullPredicate(IsNullPredicate node, 
Context context) {
-    ColumnTransformer res =
-        context.cache.computeIfAbsent(
-            node,
-            n -> {
-              if (context.hasSeen.containsKey(node)) {
-                IdentityColumnTransformer identity =
-                    new IdentityColumnTransformer(
-                        BOOLEAN, context.originSize + 
context.commonTransformerList.size());
-                ColumnTransformer columnTransformer = 
context.hasSeen.get(node);
-                columnTransformer.addReferenceCount();
-                context.commonTransformerList.add(columnTransformer);
-                context.leafList.add(identity);
-                context.inputDataTypes.add(TSDataType.BOOLEAN);
-                return identity;
-              } else {
-                ColumnTransformer childColumnTransformer = 
process(node.getValue(), context);
-                return new IsNullColumnTransformer(BOOLEAN, 
childColumnTransformer, false);
-              }
-            });
+    if (!context.cache.containsKey(node)) {
+      if (context.hasSeen.containsKey(node)) {
+        IdentityColumnTransformer identity =
+            new IdentityColumnTransformer(
+                BOOLEAN, context.originSize + 
context.commonTransformerList.size());
+        ColumnTransformer columnTransformer = context.hasSeen.get(node);
+        columnTransformer.addReferenceCount();
+        context.commonTransformerList.add(columnTransformer);
+        context.leafList.add(identity);
+        context.inputDataTypes.add(TSDataType.BOOLEAN);
+        context.cache.put(node, identity);
+      } else {
+        ColumnTransformer childColumnTransformer = process(node.getValue(), 
context);
+        context.cache.put(
+            node, new IsNullColumnTransformer(BOOLEAN, childColumnTransformer, 
false));
+      }
+    }
+    ColumnTransformer res = context.cache.get(node);
     res.addReferenceCount();
     return res;
   }
@@ -638,6 +812,14 @@ public class ColumnTransformerBuilder
     throw new 
UnsupportedOperationException(String.format(UNSUPPORTED_EXPRESSION, node));
   }
 
+  public static boolean isLongLiteral(Expression expression) {
+    return expression instanceof LongLiteral;
+  }
+
+  public static boolean isBooleanLiteral(Expression expression) {
+    return expression instanceof BooleanLiteral;
+  }
+
   public static class Context {
 
     private final SessionInfo sessionInfo;
@@ -663,6 +845,8 @@ public class ColumnTransformerBuilder
 
     private final TypeProvider typeProvider;
 
+    private final Metadata metadata;
+
     public Context(
         SessionInfo sessionInfo,
         List<LeafColumnTransformer> leafList,
@@ -672,7 +856,8 @@ public class ColumnTransformerBuilder
         List<ColumnTransformer> commonTransformerList,
         List<TSDataType> inputDataTypes,
         int originSize,
-        TypeProvider typeProvider) {
+        TypeProvider typeProvider,
+        Metadata metadata) {
       this.sessionInfo = sessionInfo;
       this.leafList = leafList;
       this.inputLocations = inputLocations;
@@ -682,6 +867,7 @@ public class ColumnTransformerBuilder
       this.inputDataTypes = inputDataTypes;
       this.originSize = originSize;
       this.typeProvider = typeProvider;
+      this.metadata = metadata;
     }
 
     public Type getType(SymbolReference symbolReference) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
index 2894ac2b432..5fa313e747b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
@@ -31,6 +31,8 @@ import 
org.apache.iotdb.db.queryengine.execution.operator.Operator;
 import org.apache.iotdb.db.queryengine.metric.QueryRelatedResourceMetricSet;
 import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
+import 
org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadataImpl;
 import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion;
 import org.apache.iotdb.db.utils.SetThreadName;
 
@@ -51,6 +53,8 @@ public class LocalExecutionPlanner {
   private static final long ALLOCATE_MEMORY_FOR_OPERATORS;
   private static final long MAX_REST_MEMORY_FOR_LOAD;
 
+  public final Metadata metadata = new TableMetadataImpl();
+
   static {
     IoTDBConfig CONFIG = IoTDBDescriptor.getInstance().getConfig();
     ALLOCATE_MEMORY_FOR_OPERATORS = CONFIG.getAllocateMemoryForOperators();
@@ -88,7 +92,7 @@ public class LocalExecutionPlanner {
         root = plan.accept(new OperatorTreeGenerator(), context);
         break;
       case TABLE:
-        root = plan.accept(new TableOperatorGenerator(), context);
+        root = plan.accept(new TableOperatorGenerator(metadata), context);
         break;
       default:
         throw new IllegalArgumentException(String.format("Unknown sql dialect: 
%s", sqlDialect));
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
index bd17059f141..e9d17b2d21d 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
@@ -45,6 +45,7 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.sink.IdentitySinkN
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.InputLocation;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.SeriesScanOptions;
 import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnSchema;
+import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.LimitNode;
@@ -87,6 +88,12 @@ import static 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeM
 /** This Visitor is responsible for transferring Table PlanNode Tree to Table 
Operator Tree. */
 public class TableOperatorGenerator extends PlanVisitor<Operator, 
LocalExecutionPlanContext> {
 
+  private final Metadata metadata;
+
+  public TableOperatorGenerator(Metadata metadata) {
+    this.metadata = metadata;
+  }
+
   @Override
   public Operator visitPlan(PlanNode node, LocalExecutionPlanContext context) {
     throw new UnsupportedOperationException("should call the concrete 
visitXX() method");
@@ -336,7 +343,8 @@ public class TableOperatorGenerator extends 
PlanVisitor<Operator, LocalExecution
                           ImmutableList.of(),
                           ImmutableList.of(),
                           0,
-                          context.getTypeProvider());
+                          context.getTypeProvider(),
+                          metadata);
 
                   return visitor.process(p, filterColumnTransformerContext);
                 })
@@ -362,7 +370,8 @@ public class TableOperatorGenerator extends 
PlanVisitor<Operator, LocalExecution
             commonTransformerList,
             filterOutputDataTypes,
             inputLocations.size() - 1,
-            context.getTypeProvider());
+            context.getTypeProvider(),
+            metadata);
 
     for (Expression expression : projectExpressions) {
       projectOutputTransformerList.add(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java
index bac96ac74f5..0ea15d2f79a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java
@@ -47,6 +47,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static 
org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand.TIMESTAMP_EXPRESSION_STRING;
 
 public class ConvertPredicateToTimeFilterVisitor extends 
PredicateVisitor<Filter, Void> {
 
@@ -210,7 +211,7 @@ public class ConvertPredicateToTimeFilterVisitor extends 
PredicateVisitor<Filter
 
   public static boolean isTimeColumn(Expression expression) {
     return expression instanceof SymbolReference
-        && "time".equalsIgnoreCase(((SymbolReference) expression).getName());
+        && TIMESTAMP_EXPRESSION_STRING.equalsIgnoreCase(((SymbolReference) 
expression).getName());
   }
 
   public static long getLongValue(Expression expression) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
index af519b94a23..4b4dfcad85f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
@@ -108,18 +108,42 @@ public class TableMetadataImpl implements Metadata {
   public Type getFunctionReturnType(String functionName, List<? extends Type> 
argumentTypes) {
 
     // builtin scalar function
-    if 
(BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName)
-        || 
BuiltinScalarFunction.ROUND.getFunctionName().equalsIgnoreCase(functionName)) {
-      if (!isOneNumericType(argumentTypes)) {
+    if 
(BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName)) {
+      if (!isOneNumericType(argumentTypes)
+          && !(argumentTypes.size() == 2
+              && isNumericType(argumentTypes.get(0))
+              && BOOLEAN.equals(argumentTypes.get(0)))) {
         throw new SemanticException(
             "Scalar function"
                 + functionName.toLowerCase(Locale.ENGLISH)
-                + " only supports numeric data types [INT32, INT64, FLOAT, 
DOUBLE]");
+                + " only supports one numeric data types [INT32, INT64, FLOAT, 
DOUBLE] and one boolean");
+      }
+
+    } else if 
(BuiltinScalarFunction.ROUND.getFunctionName().equalsIgnoreCase(functionName)) {
+      if (!isOneNumericType(argumentTypes) && 
!isTwoNumericType(argumentTypes)) {
+        throw new SemanticException(
+            "Scalar function"
+                + functionName.toLowerCase(Locale.ENGLISH)
+                + " only supports two numeric data types [INT32, INT64, FLOAT, 
DOUBLE]");
       }
       return DOUBLE;
-    } else if 
(BuiltinScalarFunction.REPLACE.getFunctionName().equalsIgnoreCase(functionName)
-        || 
BuiltinScalarFunction.SUBSTRING.getFunctionName().equalsIgnoreCase(functionName))
 {
-      if (!isOneTextType(argumentTypes)) {
+    } else if 
(BuiltinScalarFunction.REPLACE.getFunctionName().equalsIgnoreCase(functionName))
 {
+
+      if (!isTwoTextType(argumentTypes) && isThreeTextType(argumentTypes)) {
+        throw new SemanticException(
+            "Scalar function"
+                + functionName.toLowerCase(Locale.ENGLISH)
+                + " only supports text data type.");
+      }
+      return TEXT;
+    } else if 
(BuiltinScalarFunction.SUBSTRING.getFunctionName().equalsIgnoreCase(functionName))
 {
+      if (!(argumentTypes.size() == 2
+              && TEXT.equals(argumentTypes.get(0))
+              && isNumericType(argumentTypes.get(1)))
+          && !(argumentTypes.size() == 3
+              && TEXT.equals(argumentTypes.get(0))
+              && isNumericType(argumentTypes.get(1))
+              && isNumericType(argumentTypes.get(2)))) {
         throw new SemanticException(
             "Scalar function"
                 + functionName.toLowerCase(Locale.ENGLISH)
@@ -265,6 +289,19 @@ public class TableMetadataImpl implements Metadata {
     return argumentTypes.size() == 1 && TEXT.equals(argumentTypes.get(0));
   }
 
+  public static boolean isTwoTextType(List<? extends Type> argumentTypes) {
+    return argumentTypes.size() == 2
+        && TEXT.equals(argumentTypes.get(0))
+        && TEXT.equals(argumentTypes.get(1));
+  }
+
+  public static boolean isThreeTextType(List<? extends Type> argumentTypes) {
+    return argumentTypes.size() == 3
+        && TEXT.equals(argumentTypes.get(0))
+        && TEXT.equals(argumentTypes.get(1))
+        && TEXT.equals(argumentTypes.get(2));
+  }
+
   public static boolean isNumericType(Type type) {
     return DOUBLE.equals(type) || FLOAT.equals(type) || INT32.equals(type) || 
INT64.equals(type);
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
index de83b409e8a..f4401c37a2a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
@@ -21,8 +21,6 @@ package 
org.apache.iotdb.db.queryengine.transformation.dag.column.ternary;
 
 import 
org.apache.iotdb.db.queryengine.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;
 
@@ -35,21 +33,6 @@ public abstract class CompareTernaryColumnTransformer 
extends TernaryColumnTrans
     super(returnType, firstColumnTransformer, secondColumnTransformer, 
thirdColumnTransformer);
   }
 
-  @Override
-  public void evaluate() {
-    firstColumnTransformer.tryEvaluate();
-    secondColumnTransformer.tryEvaluate();
-    thirdColumnTransformer.tryEvaluate();
-    // attention: get positionCount before calling getColumn
-    int positionCount = firstColumnTransformer.getColumnCachePositionCount();
-    Column firstColumn = firstColumnTransformer.getColumn();
-    Column secondColumn = secondColumnTransformer.getColumn();
-    Column thirdColumn = thirdColumnTransformer.getColumn();
-    ColumnBuilder columnBuilder = 
returnType.createColumnBuilder(positionCount);
-    doTransform(firstColumn, secondColumn, thirdColumn, columnBuilder, 
positionCount);
-    initializeColumnCache(columnBuilder.build());
-  }
-
   @Override
   protected final void checkType() {
     if (firstColumnTransformer.isReturnTypeNumeric()
@@ -64,11 +47,4 @@ public abstract class CompareTernaryColumnTransformer 
extends TernaryColumnTrans
     throw new UnsupportedOperationException(
         "The Type of three subExpression should be all Numeric or Text");
   }
-
-  protected abstract void doTransform(
-      Column firstColumn,
-      Column secondColumn,
-      Column thirdColumn,
-      ColumnBuilder builder,
-      int positionCount);
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/TernaryColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/TernaryColumnTransformer.java
index 6d747d3dae8..5359e50cbb5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/TernaryColumnTransformer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/TernaryColumnTransformer.java
@@ -21,6 +21,8 @@ package 
org.apache.iotdb.db.queryengine.transformation.dag.column.ternary;
 
 import 
org.apache.iotdb.db.queryengine.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;
 
 public abstract class TernaryColumnTransformer extends ColumnTransformer {
@@ -43,6 +45,27 @@ public abstract class TernaryColumnTransformer extends 
ColumnTransformer {
     checkType();
   }
 
+  @Override
+  protected void evaluate() {
+    firstColumnTransformer.tryEvaluate();
+    secondColumnTransformer.tryEvaluate();
+    thirdColumnTransformer.tryEvaluate();
+    int positionCount = firstColumnTransformer.getColumnCachePositionCount();
+    Column firstColumn = firstColumnTransformer.getColumn();
+    Column secondColumn = secondColumnTransformer.getColumn();
+    Column thirdColumn = thirdColumnTransformer.getColumn();
+    ColumnBuilder columnBuilder = 
returnType.createColumnBuilder(positionCount);
+    doTransform(firstColumn, secondColumn, thirdColumn, columnBuilder, 
positionCount);
+    initializeColumnCache(columnBuilder.build());
+  }
+
+  protected abstract void doTransform(
+      Column firstColumn,
+      Column secondColumn,
+      Column thirdColumn,
+      ColumnBuilder builder,
+      int positionCount);
+
   public ColumnTransformer getFirstColumnTransformer() {
     return firstColumnTransformer;
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/DiffColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/DiffColumnTransformer.java
new file mode 100644
index 00000000000..dca73590f7c
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/DiffColumnTransformer.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.BinaryColumnTransformer;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.block.column.ColumnBuilder;
+import org.apache.tsfile.read.common.type.Type;
+
+public class DiffColumnTransformer extends BinaryColumnTransformer {
+
+  // cache the last non-null value
+  private double lastValue;
+
+  // indicate whether lastValue is null
+  private boolean lastValueIsNull = true;
+
+  public DiffColumnTransformer(
+      Type returnType, ColumnTransformer leftTransformer, ColumnTransformer 
rightTransformer) {
+    super(returnType, leftTransformer, rightTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    // do nothing
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn, Column rightColumn, ColumnBuilder builder, int 
positionCount) {
+    Type leftType = leftTransformer.getType();
+    Type rightType = rightTransformer.getType();
+    for (int i = 0; i < positionCount; i++) {
+      if (leftColumn.isNull(i)) {
+        builder.appendNull(); // currValue is null, append null
+
+        // When currValue is null:
+        // ignoreNull = true, keep lastValueIsNull as before
+        // ignoreNull = false or ignoreNull is null, update lastValueIsNull to 
true
+        lastValueIsNull |= (rightColumn.isNull(i) || 
!rightType.getBoolean(rightColumn, i));
+      } else {
+        double currValue = leftType.getDouble(leftColumn, i);
+        if (lastValueIsNull) {
+          builder.appendNull(); // lastValue is null, append null
+        } else {
+          returnType.writeDouble(builder, currValue - lastValue);
+        }
+
+        lastValue = currValue; // currValue is not null, update lastValue
+        lastValueIsNull = false;
+      }
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace2ColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace2ColumnTransformer.java
new file mode 100644
index 00000000000..defcbfaf2fd
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace2ColumnTransformer.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.BinaryColumnTransformer;
+
+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.utils.BytesUtils;
+
+public class Replace2ColumnTransformer extends BinaryColumnTransformer {
+
+  public Replace2ColumnTransformer(
+      Type returnType, ColumnTransformer leftTransformer, ColumnTransformer 
rightTransformer) {
+    super(returnType, leftTransformer, rightTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    // do nothing
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn, Column rightColumn, ColumnBuilder builder, int 
positionCount) {
+    Type leftType = leftTransformer.getType();
+    Type rightType = rightTransformer.getType();
+    for (int i = 0, n = leftColumn.getPositionCount(); i < n; i++) {
+      if (!leftColumn.isNull(i) && !rightColumn.isNull(i)) {
+        returnType.writeBinary(
+            builder,
+            BytesUtils.valueOf(
+                leftType
+                    .getBinary(leftColumn, i)
+                    .getStringValue(TSFileConfig.STRING_CHARSET)
+                    .replace(
+                        rightType
+                            .getBinary(rightColumn, i)
+                            .getStringValue(TSFileConfig.STRING_CHARSET),
+                        "")));
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace3ColumnTransformer.java
similarity index 50%
copy from 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
copy to 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace3ColumnTransformer.java
index de83b409e8a..d8cddbe5124 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/CompareTernaryColumnTransformer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/Replace3ColumnTransformer.java
@@ -17,17 +17,19 @@
  * under the License.
  */
 
-package org.apache.iotdb.db.queryengine.transformation.dag.column.ternary;
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
 
 import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ternary.TernaryColumnTransformer;
 
 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.BytesUtils;
 
-public abstract class CompareTernaryColumnTransformer extends 
TernaryColumnTransformer {
-  protected CompareTernaryColumnTransformer(
+public class Replace3ColumnTransformer extends TernaryColumnTransformer {
+  public Replace3ColumnTransformer(
       Type returnType,
       ColumnTransformer firstColumnTransformer,
       ColumnTransformer secondColumnTransformer,
@@ -36,39 +38,38 @@ public abstract class CompareTernaryColumnTransformer 
extends TernaryColumnTrans
   }
 
   @Override
-  public void evaluate() {
-    firstColumnTransformer.tryEvaluate();
-    secondColumnTransformer.tryEvaluate();
-    thirdColumnTransformer.tryEvaluate();
-    // attention: get positionCount before calling getColumn
-    int positionCount = firstColumnTransformer.getColumnCachePositionCount();
-    Column firstColumn = firstColumnTransformer.getColumn();
-    Column secondColumn = secondColumnTransformer.getColumn();
-    Column thirdColumn = thirdColumnTransformer.getColumn();
-    ColumnBuilder columnBuilder = 
returnType.createColumnBuilder(positionCount);
-    doTransform(firstColumn, secondColumn, thirdColumn, columnBuilder, 
positionCount);
-    initializeColumnCache(columnBuilder.build());
-  }
-
-  @Override
-  protected final void checkType() {
-    if (firstColumnTransformer.isReturnTypeNumeric()
-            && secondColumnTransformer.isReturnTypeNumeric()
-            && thirdColumnTransformer.isReturnTypeNumeric()
-        || firstColumnTransformer.typeEquals(TypeEnum.TEXT)
-            && secondColumnTransformer.typeEquals(TypeEnum.TEXT)
-            && thirdColumnTransformer.typeEquals(TypeEnum.TEXT)) {
-      return;
-    }
-
-    throw new UnsupportedOperationException(
-        "The Type of three subExpression should be all Numeric or Text");
-  }
-
-  protected abstract void doTransform(
+  protected void doTransform(
       Column firstColumn,
       Column secondColumn,
       Column thirdColumn,
       ColumnBuilder builder,
-      int positionCount);
+      int positionCount) {
+    Type firstType = firstColumnTransformer.getType();
+    Type secondType = secondColumnTransformer.getType();
+    Type thirdType = thirdColumnTransformer.getType();
+    for (int i = 0, n = firstColumn.getPositionCount(); i < n; i++) {
+      if (!firstColumn.isNull(i) && !secondColumn.isNull(i) && 
!thirdColumn.isNull(i)) {
+        returnType.writeBinary(
+            builder,
+            BytesUtils.valueOf(
+                firstType
+                    .getBinary(firstColumn, i)
+                    .getStringValue(TSFileConfig.STRING_CHARSET)
+                    .replace(
+                        secondType
+                            .getBinary(secondColumn, i)
+                            .getStringValue(TSFileConfig.STRING_CHARSET),
+                        thirdType
+                            .getBinary(thirdColumn, i)
+                            .getStringValue(TSFileConfig.STRING_CHARSET))));
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+
+  @Override
+  protected void checkType() {
+    // do nothing
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
new file mode 100644
index 00000000000..ebd250cc86c
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/RoundColumnTransformer.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.BinaryColumnTransformer;
+
+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 {
+
+  public RoundColumnTransformer(
+      Type returnType, ColumnTransformer leftTransformer, ColumnTransformer 
rightTransformer) {
+    super(returnType, leftTransformer, rightTransformer);
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn, Column rightColumn, ColumnBuilder builder, int 
positionCount) {
+    TypeEnum sourceType = leftTransformer.getType().getTypeEnum();
+    Type rightType = rightTransformer.getType();
+    for (int i = 0; i < positionCount; i++) {
+      if (!leftColumn.isNull(i) && !rightColumn.isNull(i)) {
+        int places = rightType.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;
+          default:
+            throw new UnsupportedOperationException(
+                String.format("Unsupported source dataType: %s", sourceType));
+        }
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+
+  @Override
+  protected void checkType() {
+    if (!leftTransformer.isReturnTypeNumeric() || 
!rightTransformer.isReturnTypeNumeric()) {
+      throw new UnsupportedOperationException("Unsupported Type");
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString2ColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString2ColumnTransformer.java
new file mode 100644
index 00000000000..fc85b6f54a9
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString2ColumnTransformer.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.binary.BinaryColumnTransformer;
+
+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.utils.BytesUtils;
+
+import static 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.SubStringFunctionColumnTransformer.EMPTY_STRING;
+
+public class SubString2ColumnTransformer extends BinaryColumnTransformer {
+  public SubString2ColumnTransformer(
+      Type returnType, ColumnTransformer leftTransformer, ColumnTransformer 
rightTransformer) {
+    super(returnType, leftTransformer, rightTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    // do nothing
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn, Column rightColumn, ColumnBuilder builder, int 
positionCount) {
+    Type rightType = rightTransformer.getType();
+    for (int i = 0; i < positionCount; i++) {
+      if (!leftColumn.isNull(i) && !rightColumn.isNull(i)) {
+        String currentValue = 
leftColumn.getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET);
+        int beginPosition = rightType.getInt(rightColumn, i);
+        if (beginPosition >= currentValue.length()) {
+          currentValue = EMPTY_STRING;
+        } else {
+          currentValue = currentValue.substring(beginPosition);
+        }
+        builder.writeBinary(BytesUtils.valueOf(currentValue));
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString3ColumnTransformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString3ColumnTransformer.java
new file mode 100644
index 00000000000..c9a3ae1a3fe
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/SubString3ColumnTransformer.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+import 
org.apache.iotdb.db.queryengine.transformation.dag.column.ternary.TernaryColumnTransformer;
+
+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.utils.BytesUtils;
+
+import static 
org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar.SubStringFunctionColumnTransformer.EMPTY_STRING;
+
+public class SubString3ColumnTransformer extends TernaryColumnTransformer {
+  public SubString3ColumnTransformer(
+      Type returnType,
+      ColumnTransformer firstColumnTransformer,
+      ColumnTransformer secondColumnTransformer,
+      ColumnTransformer thirdColumnTransformer) {
+    super(returnType, firstColumnTransformer, secondColumnTransformer, 
thirdColumnTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    // do nothing
+  }
+
+  @Override
+  protected void doTransform(
+      Column firstColumn,
+      Column secondColumn,
+      Column thirdColumn,
+      ColumnBuilder builder,
+      int positionCount) {
+    Type firstType = firstColumnTransformer.getType();
+    Type secondType = secondColumnTransformer.getType();
+    Type thirdType = thirdColumnTransformer.getType();
+    for (int i = 0; i < positionCount; i++) {
+      if (!firstColumn.isNull(i) && !secondColumn.isNull(i) && 
!thirdColumn.isNull(i)) {
+        String currentValue =
+            firstType.getBinary(firstColumn, 
i).getStringValue(TSFileConfig.STRING_CHARSET);
+        int beginPosition = secondType.getInt(secondColumn, i);
+        int endPosition = thirdType.getInt(thirdColumn, i);
+        if (beginPosition >= currentValue.length() || endPosition < 0) {
+          currentValue = EMPTY_STRING;
+        } else {
+          if (endPosition >= currentValue.length()) {
+            currentValue = currentValue.substring(beginPosition);
+          } else {
+            currentValue = currentValue.substring(beginPosition, endPosition);
+          }
+        }
+        builder.writeBinary(BytesUtils.valueOf(currentValue));
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+}

Reply via email to