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

ycycse pushed a commit to branch ycy/planCacheDemo
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/ycy/planCacheDemo by this push:
     new 4e73f800048 support basic procedure of plan cache PS: the replacement 
strategy is not good enough which doesn't support multi-thread
4e73f800048 is described below

commit 4e73f800048f2120fa47572108ae9ba13cdf0e7a
Author: YangCaiyin <[email protected]>
AuthorDate: Fri Feb 21 21:25:58 2025 +0800

    support basic procedure of plan cache
    PS: the replacement strategy is not good enough which doesn't support 
multi-thread
---
 .../db/queryengine/common/MPPQueryContext.java     |  31 ++++
 .../plan/relational/planner/CachedValue.java       |  63 ++++++++
 .../plan/relational/planner/PlanCacheManager.java  |  50 ++++++
 .../plan/relational/planner/SymbolAllocator.java   |  13 ++
 .../relational/planner/TableLogicalPlanner.java    | 167 +++++++++++++++++++--
 .../optimizations/PushPredicateIntoTableScan.java  |  16 +-
 .../plan/relational/sql/ast/Literal.java           |   5 +
 .../plan/relational/sql/ast/LongLiteral.java       |  11 +-
 8 files changed, 333 insertions(+), 23 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
index 30007c6979c..38c81c472dd 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
@@ -28,6 +28,8 @@ import 
org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
 import org.apache.iotdb.db.queryengine.plan.analyze.lock.SchemaLockType;
 import 
org.apache.iotdb.db.queryengine.plan.planner.memory.MemoryReservationManager;
 import 
org.apache.iotdb.db.queryengine.plan.planner.memory.NotThreadSafeMemoryReservationManager;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Literal;
 import org.apache.iotdb.db.queryengine.statistics.QueryPlanStatistics;
 
 import org.apache.tsfile.read.filter.basic.Filter;
@@ -357,4 +359,33 @@ public class MPPQueryContext {
   public void setUserQuery(boolean userQuery) {
     this.userQuery = userQuery;
   }
+
+  // TODO temporary for plan cache will be optimized
+  List<Expression> metaDataExpressionList;
+  List<String> attributeColumns;
+  List<Literal> literalList;
+
+  public void setAttributeColumns(List<String> attributeColumns) {
+    this.attributeColumns = attributeColumns;
+  }
+
+  public void setMetaDataExpressionList(List<Expression> 
metaDataExpressionList) {
+    this.metaDataExpressionList = metaDataExpressionList;
+  }
+
+  public void setLiteralList(List<Literal> literalList) {
+    this.literalList = literalList;
+  }
+
+  public List<Expression> getMetaDataExpressionList() {
+    return metaDataExpressionList;
+  }
+
+  public List<String> getAttributeColumns() {
+    return attributeColumns;
+  }
+
+  public List<Literal> getLiteralList() {
+    return literalList;
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CachedValue.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CachedValue.java
new file mode 100644
index 00000000000..ceebd607782
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CachedValue.java
@@ -0,0 +1,63 @@
+package org.apache.iotdb.db.queryengine.plan.relational.planner;
+
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Literal;
+
+import org.apache.tsfile.read.common.type.Type;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class CachedValue {
+
+  PlanNode planNode;
+
+  DatasetHeader respHeader;
+  HashMap<Symbol, Type> symbolMap;
+
+  // Used for indexScan to fetch device
+  List<Expression> metadataExpressionList;
+  List<String> attributeColumns;
+  List<Literal> literalReference;
+
+  public CachedValue(
+      PlanNode planNode,
+      List<Literal> literalReference,
+      DatasetHeader header,
+      HashMap<Symbol, Type> symbolMap,
+      List<Expression> metadataExpressionList,
+      List<String> attributeColumns) {
+    this.planNode = planNode;
+    this.respHeader = header;
+    this.symbolMap = symbolMap;
+    this.metadataExpressionList = metadataExpressionList;
+    this.attributeColumns = attributeColumns;
+    this.literalReference = literalReference;
+  }
+
+  public DatasetHeader getRespHeader() {
+    return respHeader;
+  }
+
+  public PlanNode getPlanNode() {
+    return planNode;
+  }
+
+  public HashMap<Symbol, Type> getSymbolMap() {
+    return symbolMap;
+  }
+
+  public List<Expression> getMetadataExpressionList() {
+    return metadataExpressionList;
+  }
+
+  public List<String> getAttributeColumns() {
+    return attributeColumns;
+  }
+
+  public List<Literal> getLiteralReference() {
+    return literalReference;
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanCacheManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanCacheManager.java
new file mode 100644
index 00000000000..25559d6fd20
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanCacheManager.java
@@ -0,0 +1,50 @@
+package org.apache.iotdb.db.queryengine.plan.relational.planner;
+
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Literal;
+
+import org.apache.tsfile.read.common.type.Type;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PlanCacheManager {
+
+  private static class SingletonHolder {
+    private static final PlanCacheManager INSTANCE = new PlanCacheManager();
+  }
+
+  private final Map<String, CachedValue> planCache;
+
+  private PlanCacheManager() {
+    planCache = new ConcurrentHashMap<>();
+  }
+
+  public static PlanCacheManager getInstance() {
+    return SingletonHolder.INSTANCE;
+  }
+
+  public void cacheValue(
+      String cachedKey,
+      PlanNode planNodeTree,
+      List<Literal> literalReference,
+      DatasetHeader header,
+      HashMap<Symbol, Type> symbolMap,
+      List<Expression> expressionList,
+      List<String> columnAttributes) {
+    planCache.put(
+        cachedKey,
+        new CachedValue(
+            planNodeTree, literalReference, header, symbolMap, expressionList, 
columnAttributes));
+  }
+
+  public CachedValue getCachedValue(String cacheKey) {
+    return planCache.get(cacheKey);
+  }
+
+  // TODO add LRU strategy
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
index fa0cdd72f88..ff20e8f528b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
@@ -105,7 +105,20 @@ public class SymbolAllocator {
     return TypeProvider.viewOf(symbolMap);
   }
 
+  public HashMap<Symbol, Type> cloneSymbolMap() {
+    return new HashMap<>(symbolMap);
+  }
+
+  public void fill(Map<Symbol, Type> symbolMap) {
+    this.symbolMap.putAll(symbolMap);
+    nextId = -1;
+  }
+
   private int nextId() {
+    if (nextId == -1) {
+      throw new UnsupportedOperationException(
+          "This symbolAllocator can't allocate any symbol anymore because it 
is generated by cache value");
+    }
     return nextId++;
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
index 0e2c877ae5b..769c489e52d 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
@@ -19,10 +19,15 @@
 
 package org.apache.iotdb.db.queryengine.plan.relational.planner;
 
+import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
+import org.apache.iotdb.commons.partition.DataPartition;
+import org.apache.iotdb.commons.partition.DataPartitionQueryParam;
 import org.apache.iotdb.commons.partition.SchemaPartition;
 import org.apache.iotdb.commons.schema.column.ColumnHeader;
 import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.conf.IoTDBConfig;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
 import org.apache.iotdb.db.queryengine.common.QueryId;
 import org.apache.iotdb.db.queryengine.common.SessionInfo;
@@ -37,13 +42,18 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.pipe.PipeEnrichedW
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
+import 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.ConvertPredicateToTimeFilterVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.relational.execution.querystats.PlanOptimizersStatsCollector;
+import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry;
 import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
+import 
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ReplaceSymbolInExpression;
+import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.LimitNode;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OffsetNode;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode;
+import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.CreateOrUpdateTableDeviceNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.TableDeviceAttributeUpdateNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.TableDeviceFetchNode;
@@ -59,6 +69,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainAnalyze;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.FetchDevice;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Literal;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LiteralMarkerReplacer;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LoadTsFile;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PipeEnriched;
@@ -77,19 +88,22 @@ import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.type.LongType;
 import org.apache.tsfile.read.common.type.StringType;
 import org.apache.tsfile.read.common.type.TypeFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.utils.Pair;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 import static java.util.Objects.requireNonNull;
 import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.LOGICAL_PLANNER;
 import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.LOGICAL_PLAN_OPTIMIZE;
+import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.PARTITION_FETCHER;
 import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.TABLE_TYPE;
+import static 
org.apache.iotdb.db.queryengine.plan.analyze.AnalyzeVisitor.getTimePartitionSlotList;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice.COUNT_DEVICE_HEADER_STRING;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice.getDeviceColumnHeaderList;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
@@ -101,8 +115,6 @@ public class TableLogicalPlanner {
   private final List<PlanOptimizer> planOptimizers;
   private final Metadata metadata;
   private final WarningCollector warningCollector;
-  private final Logger logger = 
LoggerFactory.getLogger(TableLogicalPlanner.class);
-  private final HashMap<String, PlanNode> planCache = new HashMap<String, 
PlanNode>();
 
   @TestOnly
   public TableLogicalPlanner(
@@ -136,9 +148,10 @@ public class TableLogicalPlanner {
     this.planOptimizers = planOptimizers;
   }
 
-  private void generalizeStatement(Query query) {
+  private List<Literal> generalizeStatement(Query query) {
     LiteralMarkerReplacer literalMarkerReplacer = new LiteralMarkerReplacer();
     literalMarkerReplacer.process(query);
+    return literalMarkerReplacer.getLiteralList();
   }
 
   private String calculateCacheKey(Statement statement, Analysis analysis) {
@@ -149,21 +162,135 @@ public class TableLogicalPlanner {
     return sb.toString();
   }
 
+  private static final IoTDBConfig CONFIG = 
IoTDBDescriptor.getInstance().getConfig();
+
+  private DataPartition fetchDataPartitionByDevices(
+      final String
+          database, // for tree view, database should be the real tree db name 
with `root.` prefix
+      final List<DeviceEntry> deviceEntries,
+      final Filter globalTimeFilter) {
+    final Pair<List<TTimePartitionSlot>, Pair<Boolean, Boolean>> res =
+        getTimePartitionSlotList(globalTimeFilter, queryContext);
+
+    // there is no satisfied time range
+    if (res.left.isEmpty() && Boolean.FALSE.equals(res.right.left)) {
+      return new DataPartition(
+          Collections.emptyMap(),
+          CONFIG.getSeriesPartitionExecutorClass(),
+          CONFIG.getSeriesPartitionSlotNum());
+    }
+
+    final List<DataPartitionQueryParam> dataPartitionQueryParams =
+        deviceEntries.stream()
+            .map(
+                deviceEntry ->
+                    new DataPartitionQueryParam(
+                        deviceEntry.getDeviceID(), res.left, res.right.left, 
res.right.right))
+            .collect(Collectors.toList());
+
+    if (res.right.left || res.right.right) {
+      return metadata.getDataPartitionWithUnclosedTimeRange(database, 
dataPartitionQueryParams);
+    } else {
+      return metadata.getDataPartition(database, dataPartitionQueryParams);
+    }
+  }
+
+  private void adjustBySchema(PlanNode planNode, CachedValue cachedValue, 
Analysis analysis) {
+    if (!(planNode instanceof DeviceTableScanNode)) {
+      for (PlanNode child : planNode.getChildren()) {
+        adjustBySchema(child, cachedValue, analysis);
+      }
+      return;
+    }
+    DeviceTableScanNode deviceTableScanNode = (DeviceTableScanNode) planNode;
+    final List<DeviceEntry> deviceEntries =
+        metadata.indexScan(
+            deviceTableScanNode.getQualifiedObjectName(),
+            cachedValue.getMetadataExpressionList().stream()
+                .map(
+                    expression ->
+                        ReplaceSymbolInExpression.transform(
+                            expression, deviceTableScanNode.getAssignments()))
+                .collect(Collectors.toList()),
+            cachedValue.getAttributeColumns(),
+            queryContext);
+    deviceTableScanNode.setDeviceEntries(deviceEntries);
+
+    if (deviceEntries.isEmpty()) {
+      if (analysis.noAggregates() && !analysis.hasJoinNode()) {
+        // no device entries, queries(except aggregation and join) can be 
finished
+        analysis.setEmptyDataSource(true);
+        analysis.setFinishQueryAfterAnalyze();
+      }
+    } else {
+      final Filter timeFilter =
+          deviceTableScanNode
+              .getTimePredicate()
+              .map(value -> value.accept(new 
ConvertPredicateToTimeFilterVisitor(), null))
+              .orElse(null);
+
+      deviceTableScanNode.setTimeFilter(timeFilter);
+
+      long startTime = System.nanoTime();
+      final DataPartition dataPartition =
+          fetchDataPartitionByDevices(
+              // for tree view, we need to pass actual tree db name to this 
method
+              deviceTableScanNode instanceof TreeDeviceViewScanNode
+                  ? ((TreeDeviceViewScanNode) 
deviceTableScanNode).getTreeDBName()
+                  : 
deviceTableScanNode.getQualifiedObjectName().getDatabaseName(),
+              deviceEntries,
+              timeFilter);
+
+      if (dataPartition.getDataPartitionMap().size() > 1) {
+        throw new IllegalStateException(
+            "Table model can only process data only in one database yet!");
+      }
+
+      if (dataPartition.getDataPartitionMap().isEmpty()) {
+        if (analysis.noAggregates() && !analysis.hasJoinNode()) {
+          // no data partitions, queries(except aggregation and join) can be 
finished
+          analysis.setEmptyDataSource(true);
+          analysis.setFinishQueryAfterAnalyze();
+        }
+      } else {
+        analysis.upsertDataPartition(dataPartition);
+      }
+
+      final long fetchPartitionCost = System.nanoTime() - startTime;
+      QueryPlanCostMetricSet.getInstance()
+          .recordPlanCost(TABLE_TYPE, PARTITION_FETCHER, fetchPartitionCost);
+      queryContext.setFetchPartitionCost(fetchPartitionCost);
+    }
+  }
+
   public LogicalQueryPlan plan(final Analysis analysis) {
     long startTime = System.nanoTime();
     Statement statement = analysis.getStatement();
 
     // Try to use plan cache
-    // We should check if statement is Query in enablePlanCache() method
-    generalizeStatement((Query) statement);
-
-    String cachedKey = calculateCacheKey(statement, analysis);
-    PlanNode cachedPlan = planCache.get(cachedKey);
-    if (cachedPlan != null) {
-      // deal with the device stuff
-      return new LogicalQueryPlan(queryContext, cachedPlan);
+    // We should check if statement gis Query in enablePlanCache() method\
+    String cachedKey = "";
+
+    List<Literal> literalReference = null;
+    if (statement instanceof Query) {
+      List<Literal> literalList = generalizeStatement((Query) statement);
+      cachedKey = calculateCacheKey(statement, analysis);
+      CachedValue cachedValue = 
PlanCacheManager.getInstance().getCachedValue(cachedKey);
+      if (cachedValue != null) {
+        // deal with the device stuff
+        symbolAllocator.fill(cachedValue.getSymbolMap());
+        analysis.setRespDatasetHeader(cachedValue.getRespHeader());
+        adjustBySchema(cachedValue.planNode, cachedValue, analysis);
+
+        for (int i = 0; i < cachedValue.getLiteralReference().size(); i++) {
+          cachedValue.getLiteralReference().get(i).replace(literalList.get(i));
+        }
+
+        return new LogicalQueryPlan(queryContext, cachedValue.getPlanNode());
+      }
+      // Following implementation of plan should be based on the 
generalizedStatement
+      literalReference = literalList;
     }
-    // Following implementation of plan should be based on the 
generalizedStatement
 
     PlanNode planNode = planStatement(analysis, statement);
 
@@ -196,6 +323,16 @@ public class TableLogicalPlanner {
       queryContext.setLogicalOptimizationCost(logicalOptimizationCost);
       QueryPlanCostMetricSet.getInstance()
           .recordPlanCost(TABLE_TYPE, LOGICAL_PLAN_OPTIMIZE, 
logicalOptimizationCost);
+
+      PlanCacheManager.getInstance()
+          .cacheValue(
+              cachedKey,
+              planNode,
+              literalReference,
+              analysis.getRespDatasetHeader(),
+              symbolAllocator.cloneSymbolMap(),
+              queryContext.getMetaDataExpressionList(),
+              queryContext.getAttributeColumns());
     }
 
     return new LogicalQueryPlan(queryContext, planNode);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
index dc943f5262d..dec9834eb15 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
@@ -558,16 +558,20 @@ public class PushPredicateIntoTableScan implements 
PlanOptimizer {
         }
       }
 
+      List<Expression> metaDataExpression =
+          metadataExpressions.stream()
+              .map(
+                  expression ->
+                      ReplaceSymbolInExpression.transform(
+                          expression, tableScanNode.getAssignments()))
+              .collect(Collectors.toList());
+      queryContext.setMetaDataExpressionList(metaDataExpression);
+      queryContext.setAttributeColumns(attributeColumns);
       long startTime = System.nanoTime();
       final List<DeviceEntry> deviceEntries =
           metadata.indexScan(
               tableScanNode.getQualifiedObjectName(),
-              metadataExpressions.stream()
-                  .map(
-                      expression ->
-                          ReplaceSymbolInExpression.transform(
-                              expression, tableScanNode.getAssignments()))
-                  .collect(Collectors.toList()),
+              metaDataExpression,
               attributeColumns,
               queryContext);
       tableScanNode.setDeviceEntries(deviceEntries);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Literal.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Literal.java
index 6286f9c978b..a145dd4f32b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Literal.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Literal.java
@@ -59,4 +59,9 @@ public abstract class Literal extends Expression {
   public boolean isLiteralMarker() {
     return literalIndex != -1;
   }
+
+  public void replace(Literal literal) {
+    throw new UnsupportedOperationException("Literal Replacement is not 
supported in current type");
+  }
+  ;
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LongLiteral.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LongLiteral.java
index f9b8a14c035..c42e36e6c13 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LongLiteral.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LongLiteral.java
@@ -31,8 +31,8 @@ import static java.util.Objects.requireNonNull;
 
 public class LongLiteral extends Literal {
 
-  private final String value;
-  private final long parsedValue;
+  private String value;
+  private long parsedValue;
 
   public LongLiteral(String value) {
     super(null);
@@ -134,4 +134,11 @@ public class LongLiteral extends Literal {
   public Object getTsValue() {
     return parsedValue;
   }
+
+  @Override
+  public void replace(Literal literal) {
+    LongLiteral longLiteral = (LongLiteral) literal;
+    this.value = longLiteral.getValue();
+    this.parsedValue = longLiteral.getParsedValue();
+  }
 }

Reply via email to