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();
+ }
}