You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ca...@apache.org on 2024/04/11 03:25:08 UTC
(iotdb) branch ty/TableModelGrammar updated: add SimplifyExpressions and NormalizeOrExpressionRewriter
This is an automated email from the ASF dual-hosted git repository.
caogaofei 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 8b24378c7fb add SimplifyExpressions and NormalizeOrExpressionRewriter
8b24378c7fb is described below
commit 8b24378c7fbc94754c675573f64e940575837e20
Author: Beyyes <cg...@foxmail.com>
AuthorDate: Thu Apr 11 11:24:49 2024 +0800
add SimplifyExpressions and NormalizeOrExpressionRewriter
---
.../plan/planner/plan/node/PlanNode.java | 5 +
.../plan/planner/plan/node/PlanVisitor.java | 11 +
.../plan/relational/planner/LogicalPlanner.java | 15 +-
.../plan/relational/planner/RelationPlanner.java | 63 ++-
.../relational/planner/RelationalPlanVisitor.java | 77 +++
.../relational/planner/ir/ExpressionRewriter.java | 142 ++++++
.../planner/ir/ExpressionTreeRewriter.java | 563 +++++++++++++++++++++
.../plan/relational/planner/ir/IrUtils.java | 261 ++++++++++
.../plan/relational/planner/ir/IrVisitor.java | 136 +++++
.../planner/ir/NormalizeOrExpressionRewriter.java | 136 +++++
.../plan/relational/planner/node/FilterNode.java | 35 +-
.../relational/planner/node/MergeSortNode.java | 58 +++
.../relational/planner/node/TableScanNode.java | 6 +
.../plan/relational/planner/node/TopKNode.java | 59 +++
.../optimizations/RelationalPlanOptimizer.java | 23 +
.../planner/optimizations/SimplifyExpressions.java | 63 +++
.../iotdb/db/relational/sql/tree/Expression.java | 2 +-
.../db/relational/sql/tree/GenericDataType.java | 2 +-
18 files changed, 1619 insertions(+), 38 deletions(-)
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNode.java
index 877db9bf543..d63c60e9c11 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNode.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.queryengine.plan.planner.plan.node;
import org.apache.iotdb.commons.exception.runtime.SerializationRunTimeException;
import org.apache.iotdb.consensus.common.request.IConsensusRequest;
import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.RelationalPlanVisitor;
import org.apache.iotdb.tsfile.utils.PublicBAOS;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
@@ -127,6 +128,10 @@ public abstract class PlanNode implements IConsensusRequest {
return visitor.visitPlan(this, context);
}
+ public <R, C> R accept(RelationalPlanVisitor<R, C> visitor, C context) {
+ return visitor.visitPlan(this, context);
+ }
+
public void serialize(ByteBuffer byteBuffer) {
serializeAttributes(byteBuffer);
id.serialize(byteBuffer);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
index 608ca14df24..9e893bbc653 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
@@ -108,6 +108,7 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNod
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
@SuppressWarnings("java:S6539") // suppress "Monster class" warning
public abstract class PlanVisitor<R, C> {
@@ -507,4 +508,14 @@ public abstract class PlanVisitor<R, C> {
public R visitPipeOperateSchemaQueueNode(PipeOperateSchemaQueueNode node, C context) {
return visitPlan(node, context);
}
+
+ // =============================== Used for Relation Model ====================================
+ public R visitFilter(
+ org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode node, C context) {
+ return visitSingleChildProcess(node, context);
+ }
+
+ public R visitTableScan(TableScanNode node, C context) {
+ return visitPlan(node, context);
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
index 1df569c2967..f11c0d86f98 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
@@ -24,6 +24,8 @@ 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.metadata.Metadata;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.RelationalPlanOptimizer;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SimplifyExpressions;
import org.apache.iotdb.db.relational.sql.tree.Query;
import org.apache.iotdb.db.relational.sql.tree.Statement;
import org.apache.iotdb.db.relational.sql.tree.Table;
@@ -32,6 +34,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.airlift.log.Logger;
+import java.util.ArrayList;
+import java.util.List;
+
import static java.util.Objects.requireNonNull;
public class LogicalPlanner {
@@ -39,6 +44,7 @@ public class LogicalPlanner {
private final MPPQueryContext context;
private final SessionInfo sessionInfo;
private final SymbolAllocator symbolAllocator = new SymbolAllocator();
+ private final List<RelationalPlanOptimizer> relationalPlanOptimizers;
private final Metadata metadata;
private final WarningCollector warningCollector;
@@ -51,10 +57,17 @@ public class LogicalPlanner {
this.metadata = metadata;
this.sessionInfo = requireNonNull(sessionInfo, "session is null");
this.warningCollector = requireNonNull(warningCollector, "warningCollector is null");
+
+ this.relationalPlanOptimizers = new ArrayList<>();
+ this.relationalPlanOptimizers.add(new SimplifyExpressions());
}
public LogicalQueryPlan plan(Analysis analysis) throws IoTDBException {
- return new LogicalQueryPlan(context, planStatement(analysis, analysis.getStatement()));
+ PlanNode planNode = planStatement(analysis, analysis.getStatement());
+
+ relationalPlanOptimizers.forEach(optimizer -> optimizer.optimize(planNode, analysis, context));
+
+ return new LogicalQueryPlan(context, planNode);
}
private PlanNode planStatement(Analysis analysis, Statement statement) throws IoTDBException {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
index b916c753b2e..9e180d3dc4c 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
@@ -22,11 +22,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
-import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
import org.apache.iotdb.db.relational.sql.tree.AliasedRelation;
import org.apache.iotdb.db.relational.sql.tree.AstVisitor;
-import org.apache.iotdb.db.relational.sql.tree.Expression;
import org.apache.iotdb.db.relational.sql.tree.Node;
import org.apache.iotdb.db.relational.sql.tree.Query;
import org.apache.iotdb.db.relational.sql.tree.QuerySpecification;
@@ -39,11 +37,8 @@ import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
import static java.util.Objects.requireNonNull;
-import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanBuilder.newPlanBuilder;
-import static org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.coerceIfNecessary;
class RelationPlanner extends AstVisitor<RelationPlan, Void> {
private final Analysis analysis;
@@ -124,36 +119,36 @@ class RelationPlanner extends AstVisitor<RelationPlan, Void> {
return plan;
}
- private RelationPlan addRowFilters(Table node, RelationPlan plan) {
- return addRowFilters(node, plan, Function.identity());
- }
-
- public RelationPlan addRowFilters(
- Table node, RelationPlan plan, Function<Expression, Expression> predicateTransformation) {
- List<Expression> filters = null;
- // analysis.getRowFilters(node);
-
- if (filters.isEmpty()) {
- return plan;
- }
-
- // The fields in the access control scope has the same layout as those for the table scope
- PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session);
- // .withScope(accessControlScope.apply(node), plan.getFieldMappings());
-
- for (Expression filter : filters) {
- // planBuilder = subqueryPlanner.handleSubqueries(planBuilder, filter,
- // analysis.getSubqueries(filter));
-
- Expression predicate = coerceIfNecessary(analysis, filter, filter);
- predicate = predicateTransformation.apply(predicate);
- planBuilder =
- planBuilder.withNewRoot(
- new FilterNode(idAllocator.genPlanNodeId(), planBuilder.getRoot(), predicate));
- }
+ // private RelationPlan addRowFilters(Table node, RelationPlan plan) {
+ // return addRowFilters(node, plan, Function.identity());
+ // }
- return new RelationPlan(planBuilder.getRoot(), plan.getScope(), plan.getFieldMappings());
- }
+ // public RelationPlan addRowFilters(
+ // Table node, RelationPlan plan, Function<Expression, Expression> predicateTransformation) {
+ // List<Expression> filters = null;
+ // // analysis.getRowFilters(node);
+ //
+ // if (filters.isEmpty()) {
+ // return plan;
+ // }
+ //
+ // // The fields in the access control scope has the same layout as those for the table scope
+ // PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session);
+ // // .withScope(accessControlScope.apply(node), plan.getFieldMappings());
+ //
+ // for (Expression filter : filters) {
+ // // planBuilder = subqueryPlanner.handleSubqueries(planBuilder, filter,
+ // // analysis.getSubqueries(filter));
+ //
+ // Expression predicate = coerceIfNecessary(analysis, filter, filter);
+ // predicate = predicateTransformation.apply(predicate);
+ // planBuilder =
+ // planBuilder.withNewRoot(
+ // new FilterNode(idAllocator.genPlanNodeId(), planBuilder.getRoot(), predicate));
+ // }
+ //
+ // return new RelationPlan(planBuilder.getRoot(), plan.getScope(), plan.getFieldMappings());
+ // }
// private RelationPlan addColumnMasks(Table table, RelationPlan plan) {
// Map<String, Expression> columnMasks = analysis.getColumnMasks(table);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationalPlanVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationalPlanVisitor.java
new file mode 100644
index 00000000000..e36dbddf0fa
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationalPlanVisitor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.plan.relational.planner;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.MultiChildProcessNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
+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.ProjectNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.SortNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TopKNode;
+
+public abstract class RelationalPlanVisitor<R, C> {
+
+ public abstract R visitPlan(PlanNode node, C context);
+
+ public R visitSingleChildProcess(SingleChildProcessNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitMultiChildProcess(MultiChildProcessNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitFilter(FilterNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitProject(ProjectNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitOutput(OutputNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitOffset(OffsetNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitLimit(LimitNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitTableScan(TableScanNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitSort(SortNode node, C context) {
+ return visitPlan(node, context);
+ }
+
+ public R visitTopK(TopKNode node, C context) {
+ return visitPlan(node, context);
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionRewriter.java
new file mode 100644
index 00000000000..e0d72f48928
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionRewriter.java
@@ -0,0 +1,142 @@
+/*
+ * 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.plan.relational.planner.ir;
+
+import org.apache.iotdb.db.relational.sql.tree.ArithmeticBinaryExpression;
+import org.apache.iotdb.db.relational.sql.tree.BetweenPredicate;
+import org.apache.iotdb.db.relational.sql.tree.Cast;
+import org.apache.iotdb.db.relational.sql.tree.CoalesceExpression;
+import org.apache.iotdb.db.relational.sql.tree.ComparisonExpression;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.relational.sql.tree.InPredicate;
+import org.apache.iotdb.db.relational.sql.tree.IsNullPredicate;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+import org.apache.iotdb.db.relational.sql.tree.NotExpression;
+import org.apache.iotdb.db.relational.sql.tree.NullIfExpression;
+import org.apache.iotdb.db.relational.sql.tree.Row;
+import org.apache.iotdb.db.relational.sql.tree.SearchedCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SimpleCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SymbolReference;
+
+public class ExpressionRewriter<C> {
+ protected Expression rewriteExpression(
+ Expression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return null;
+ }
+
+ public Expression rewriteRow(Row node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ // public Expression rewriteArithmeticUnary(ArithmeticNegation node, C context,
+ // ExpressionTreeRewriter<C> treeRewriter) {
+ // return rewriteExpression(node, context, treeRewriter);
+ // }
+
+ public Expression rewriteArithmeticBinary(
+ ArithmeticBinaryExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteComparisonExpression(
+ ComparisonExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteBetweenPredicate(
+ BetweenPredicate node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteLogicalExpression(
+ LogicalExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteNotExpression(
+ NotExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteIsNullPredicate(
+ IsNullPredicate node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteNullIfExpression(
+ NullIfExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteSearchedCaseExpression(
+ SearchedCaseExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteSimpleCaseExpression(
+ SimpleCaseExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteCoalesceExpression(
+ CoalesceExpression node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteFunctionCall(
+ FunctionCall node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ // public Expression rewriteLambdaExpression(LambdaExpression node, C context,
+ // ExpressionTreeRewriter<C> treeRewriter) {
+ // return rewriteExpression(node, context, treeRewriter);
+ // }
+
+ // public Expression rewriteBindExpression(BindExpression node, C context,
+ // ExpressionTreeRewriter<C> treeRewriter) {
+ // return rewriteExpression(node, context, treeRewriter);
+ // }
+
+ public Expression rewriteInPredicate(
+ InPredicate node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ // public Expression rewriteConstant(Constant node, C context, ExpressionTreeRewriter<C>
+ // treeRewriter) {
+ // return rewriteExpression(node, context, treeRewriter);
+ // }
+
+ // public Expression rewriteSubscriptExpression(SubscriptExpression node, C context,
+ // ExpressionTreeRewriter<C> treeRewriter) {
+ // return rewriteExpression(node, context, treeRewriter);
+ // }
+
+ public Expression rewriteCast(Cast node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+
+ public Expression rewriteSymbolReference(
+ SymbolReference node, C context, ExpressionTreeRewriter<C> treeRewriter) {
+ return rewriteExpression(node, context, treeRewriter);
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java
new file mode 100644
index 00000000000..b39bf4482ca
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java
@@ -0,0 +1,563 @@
+/*
+ * 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.plan.relational.planner.ir;
+
+import org.apache.iotdb.db.relational.sql.tree.ArithmeticBinaryExpression;
+import org.apache.iotdb.db.relational.sql.tree.BetweenPredicate;
+import org.apache.iotdb.db.relational.sql.tree.Cast;
+import org.apache.iotdb.db.relational.sql.tree.CoalesceExpression;
+import org.apache.iotdb.db.relational.sql.tree.ComparisonExpression;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.relational.sql.tree.InPredicate;
+import org.apache.iotdb.db.relational.sql.tree.IsNullPredicate;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+import org.apache.iotdb.db.relational.sql.tree.NotExpression;
+import org.apache.iotdb.db.relational.sql.tree.NullIfExpression;
+import org.apache.iotdb.db.relational.sql.tree.Row;
+import org.apache.iotdb.db.relational.sql.tree.SearchedCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SimpleCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SymbolReference;
+import org.apache.iotdb.db.relational.sql.tree.WhenClause;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+
+public final class ExpressionTreeRewriter<C> {
+ private final ExpressionRewriter<C> rewriter;
+ private final IrVisitor<Expression, Context<C>> visitor;
+
+ public static <T extends Expression> T rewriteWith(ExpressionRewriter<Void> rewriter, T node) {
+ return new ExpressionTreeRewriter<>(rewriter).rewrite(node, null);
+ }
+
+ public static <C, T extends Expression> T rewriteWith(
+ ExpressionRewriter<C> rewriter, T node, C context) {
+ return new ExpressionTreeRewriter<>(rewriter).rewrite(node, context);
+ }
+
+ public ExpressionTreeRewriter(ExpressionRewriter<C> rewriter) {
+ this.rewriter = rewriter;
+ this.visitor = new RewritingVisitor();
+ }
+
+ private List<Expression> rewrite(List<Expression> items, Context<C> context) {
+ ImmutableList.Builder<Expression> builder = ImmutableList.builder();
+ for (Expression expression : items) {
+ builder.add(rewrite(expression, context.get()));
+ }
+ return builder.build();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Expression> T rewrite(T node, C context) {
+ return (T) visitor.process(node, new Context<>(context, false));
+ }
+
+ /**
+ * Invoke the default rewrite logic explicitly. Specifically, it skips the invocation of the
+ * expression rewriter for the provided node.
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Expression> T defaultRewrite(T node, C context) {
+ return (T) visitor.process(node, new Context<>(context, true));
+ }
+
+ private class RewritingVisitor extends IrVisitor<Expression, Context<C>> {
+ @Override
+ protected Expression visitExpression(Expression node, Context<C> context) {
+ // RewritingVisitor must have explicit support for each expression type, with a dedicated
+ // visit method,
+ // so visitExpression() should never be called.
+ throw new UnsupportedOperationException(
+ "visit() not implemented for " + node.getClass().getName());
+ }
+
+ @Override
+ protected Expression visitRow(Row node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result = rewriter.rewriteRow(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ List<Expression> items = rewrite(node.getItems(), context);
+
+ if (!sameElements(node.getItems(), items)) {
+ return new Row(items);
+ }
+
+ return node;
+ }
+
+ // @Override
+ // protected Expression visitArithmeticNegation(ArithmeticNegation node, Context<C>
+ // context)
+ // {
+ // if (!context.isDefaultRewrite()) {
+ // Expression result = rewriter.rewriteArithmeticUnary(node, context.get(),
+ // ExpressionTreeRewriter.this);
+ // if (result != null) {
+ // return result;
+ // }
+ // }
+ //
+ // Expression child = rewrite(node.getValue(), context.get());
+ // if (child != node.getValue()) {
+ // return new ArithmeticNegation(child);
+ // }
+ //
+ // return node;
+ // }
+
+ @Override
+ public Expression visitArithmeticBinary(ArithmeticBinaryExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteArithmeticBinary(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression left = rewrite(node.getLeft(), context.get());
+ Expression right = rewrite(node.getRight(), context.get());
+
+ if (left != node.getLeft() || right != node.getRight()) {
+ // node.getLocation().get() may be null
+ return new ArithmeticBinaryExpression(
+ node.getLocation().get(), node.getOperator(), left, right);
+ }
+
+ return node;
+ }
+
+ // @Override
+ // protected Expression visitSubscriptExpression(SubscriptExpression node, Context<C>
+ // context)
+ // {
+ // if (!context.isDefaultRewrite()) {
+ // Expression result = rewriter.rewriteSubscriptExpression(node, context.get(),
+ // ExpressionTreeRewriter.this);
+ // if (result != null) {
+ // return result;
+ // }
+ // }
+ //
+ // Expression base = rewrite(node.getBase(), context.get());
+ // Expression index = rewrite(node.getIndex(), context.get());
+ //
+ // if (base != node.getBase() || index != node.getIndex()) {
+ // return new SubscriptExpression(base, index);
+ // }
+ //
+ // return node;
+ // }
+
+ @Override
+ public Expression visitComparisonExpression(ComparisonExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteComparisonExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression left = rewrite(node.getLeft(), context.get());
+ Expression right = rewrite(node.getRight(), context.get());
+
+ if (left != node.getLeft() || right != node.getRight()) {
+ return new ComparisonExpression(node.getOperator(), left, right);
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitBetweenPredicate(BetweenPredicate node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteBetweenPredicate(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression value = rewrite(node.getValue(), context.get());
+ Expression min = rewrite(node.getMin(), context.get());
+ Expression max = rewrite(node.getMax(), context.get());
+
+ if (value != node.getValue() || min != node.getMin() || max != node.getMax()) {
+ return new BetweenPredicate(value, min, max);
+ }
+
+ return node;
+ }
+
+ @Override
+ public Expression visitLogicalExpression(LogicalExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteLogicalExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ List<Expression> terms = rewrite(node.getTerms(), context);
+ if (!sameElements(node.getTerms(), terms)) {
+ return new LogicalExpression(node.getOperator(), terms);
+ }
+
+ return node;
+ }
+
+ @Override
+ public Expression visitNotExpression(NotExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteNotExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression value = rewrite(node.getValue(), context.get());
+
+ if (value != node.getValue()) {
+ return new NotExpression(value);
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitIsNullPredicate(IsNullPredicate node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteIsNullPredicate(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression value = rewrite(node.getValue(), context.get());
+
+ if (value != node.getValue()) {
+ return new IsNullPredicate(value);
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitNullIfExpression(NullIfExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteNullIfExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression first = rewrite(node.getFirst(), context.get());
+ Expression second = rewrite(node.getSecond(), context.get());
+
+ if (first != node.getFirst() || second != node.getSecond()) {
+ return new NullIfExpression(first, second);
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitSearchedCaseExpression(
+ SearchedCaseExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteSearchedCaseExpression(
+ node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ ImmutableList.Builder<WhenClause> builder = ImmutableList.builder();
+ for (WhenClause expression : node.getWhenClauses()) {
+ builder.add(rewriteWhenClause(expression, context));
+ }
+
+ Optional<Expression> defaultValue =
+ node.getDefaultValue().map(value -> rewrite(value, context.get()));
+
+ if (!sameElements(node.getDefaultValue(), defaultValue)
+ || !sameElements(node.getWhenClauses(), builder.build())) {
+ // defaultValue.get() may be null
+ return new SearchedCaseExpression(builder.build(), defaultValue.get());
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitSimpleCaseExpression(SimpleCaseExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteSimpleCaseExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression operand = rewrite(node.getOperand(), context.get());
+
+ ImmutableList.Builder<WhenClause> builder = ImmutableList.builder();
+ for (WhenClause expression : node.getWhenClauses()) {
+ builder.add(rewriteWhenClause(expression, context));
+ }
+
+ Optional<Expression> defaultValue =
+ node.getDefaultValue().map(value -> rewrite(value, context.get()));
+
+ if (operand != node.getOperand()
+ || !sameElements(node.getDefaultValue(), defaultValue)
+ || !sameElements(node.getWhenClauses(), builder.build())) {
+ // defaultValue.get() may be null
+ return new SimpleCaseExpression(operand, builder.build(), defaultValue.get());
+ }
+
+ return node;
+ }
+
+ protected WhenClause rewriteWhenClause(WhenClause node, Context<C> context) {
+ Expression operand = rewrite(node.getOperand(), context.get());
+ Expression result = rewrite(node.getResult(), context.get());
+
+ if (operand != node.getOperand() || result != node.getResult()) {
+ return new WhenClause(operand, result);
+ }
+ return node;
+ }
+
+ @Override
+ protected Expression visitCoalesceExpression(CoalesceExpression node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteCoalesceExpression(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ List<Expression> operands = rewrite(node.getOperands(), context);
+
+ if (!sameElements(node.getOperands(), operands)) {
+ return new CoalesceExpression(operands);
+ }
+
+ return node;
+ }
+
+ @Override
+ public Expression visitFunctionCall(FunctionCall node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteFunctionCall(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ List<Expression> arguments = rewrite(node.getArguments(), context);
+
+ if (!sameElements(node.getArguments(), arguments)) {
+ return new FunctionCall(node.getName(), arguments);
+ }
+ return node;
+ }
+
+ // @Override
+ // protected Expression visitLambdaExpression(LambdaExpression node, Context<C> context)
+ // {
+ // if (!context.isDefaultRewrite()) {
+ // Expression result = rewriter.rewriteLambdaExpression(node, context.get(),
+ // ExpressionTreeRewriter.this);
+ // if (result != null) {
+ // return result;
+ // }
+ // }
+ //
+ // List<String> arguments = node.getArguments().stream()
+ // .map(SymbolReference::new)
+ // .map(expression -> rewrite(expression, context.get()))
+ // .map(SymbolReference::getName)
+ // .collect(toImmutableList());
+ //
+ // Expression body = rewrite(node.getBody(), context.get());
+ // if (body != node.getBody()) {
+ // return new LambdaExpression(arguments, body);
+ // }
+ //
+ // return node;
+ // }
+
+ // @Override
+ // protected Expression visitBindExpression(BindExpression node, Context<C> context)
+ // {
+ // if (!context.isDefaultRewrite()) {
+ // Expression result = rewriter.rewriteBindExpression(node, context.get(),
+ // ExpressionTreeRewriter.this);
+ // if (result != null) {
+ // return result;
+ // }
+ // }
+ //
+ // List<Expression> values = node.getValues().stream()
+ // .map(value -> rewrite(value, context.get()))
+ // .collect(toImmutableList());
+ // Expression function = rewrite(node.getFunction(), context.get());
+ //
+ // if (!sameElements(values, node.getValues()) || (function != node.getFunction())) {
+ // return new BindExpression(values, function);
+ // }
+ // return node;
+ // }
+
+ @Override
+ public Expression visitInPredicate(InPredicate node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteInPredicate(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression value = rewrite(node.getValue(), context.get());
+ // List<Expression> values = node.getValueList().stream()
+ // .map(entry -> rewrite(entry, context.get()))
+ // .collect(toImmutableList());
+
+ if (node.getValue() != value
+ || !sameElements(Optional.of(value), Optional.of(node.getValue()))) {
+ return new InPredicate(value, value);
+ }
+
+ return node;
+ }
+
+ // @Override
+ // public Expression visitConstant(Constant node, Context<C> context)
+ // {
+ // if (!context.isDefaultRewrite()) {
+ // Expression result = rewriter.rewriteConstant(node, context.get(),
+ // ExpressionTreeRewriter.this);
+ // if (result != null) {
+ // return result;
+ // }
+ // }
+ //
+ // return node;
+ // }
+
+ @Override
+ public Expression visitCast(Cast node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result = rewriter.rewriteCast(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ Expression expression = rewrite(node.getExpression(), context.get());
+
+ if (node.getExpression() != expression) {
+ return new Cast(expression, node.getType(), node.isSafe());
+ }
+
+ return node;
+ }
+
+ @Override
+ protected Expression visitSymbolReference(SymbolReference node, Context<C> context) {
+ if (!context.isDefaultRewrite()) {
+ Expression result =
+ rewriter.rewriteSymbolReference(node, context.get(), ExpressionTreeRewriter.this);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return node;
+ }
+ }
+
+ public static class Context<C> {
+ private final boolean defaultRewrite;
+ private final C context;
+
+ private Context(C context, boolean defaultRewrite) {
+ this.context = context;
+ this.defaultRewrite = defaultRewrite;
+ }
+
+ public C get() {
+ return context;
+ }
+
+ public boolean isDefaultRewrite() {
+ return defaultRewrite;
+ }
+ }
+
+ private static <T> boolean sameElements(Optional<T> a, Optional<T> b) {
+ if (!a.isPresent() && !b.isPresent()) {
+ return true;
+ }
+ if (a.isPresent() != b.isPresent()) {
+ return false;
+ }
+
+ return a.get() == b.get();
+ }
+
+ @SuppressWarnings("ObjectEquality")
+ private static <T> boolean sameElements(Iterable<? extends T> a, Iterable<? extends T> b) {
+ if (Iterables.size(a) != Iterables.size(b)) {
+ return false;
+ }
+
+ Iterator<? extends T> first = a.iterator();
+ Iterator<? extends T> second = b.iterator();
+
+ while (first.hasNext() && second.hasNext()) {
+ if (first.next() != second.next()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java
new file mode 100644
index 00000000000..0bbd5c03d75
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed 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.plan.relational.planner.ir;
+
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toList;
+import static org.apache.iotdb.db.relational.sql.tree.BooleanLiteral.FALSE_LITERAL;
+import static org.apache.iotdb.db.relational.sql.tree.BooleanLiteral.TRUE_LITERAL;
+
+public final class IrUtils {
+ private IrUtils() {}
+
+ public static List<Expression> extractConjuncts(Expression expression) {
+ return extractPredicates(LogicalExpression.Operator.AND, expression);
+ }
+
+ public static List<Expression> extractDisjuncts(Expression expression) {
+ return extractPredicates(LogicalExpression.Operator.OR, expression);
+ }
+
+ public static List<Expression> extractPredicates(LogicalExpression expression) {
+ return extractPredicates(expression.getOperator(), expression);
+ }
+
+ public static List<Expression> extractPredicates(
+ LogicalExpression.Operator operator, Expression expression) {
+ ImmutableList.Builder<Expression> resultBuilder = ImmutableList.builder();
+ extractPredicates(operator, expression, resultBuilder);
+ return resultBuilder.build();
+ }
+
+ private static void extractPredicates(
+ LogicalExpression.Operator operator,
+ Expression expression,
+ ImmutableList.Builder<Expression> resultBuilder) {
+ if (expression instanceof LogicalExpression
+ && ((LogicalExpression) expression).getOperator() == operator) {
+ for (Expression term : ((LogicalExpression) expression).getTerms()) {
+ extractPredicates(operator, term, resultBuilder);
+ }
+ } else {
+ resultBuilder.add(expression);
+ }
+ }
+
+ public static Expression and(Expression... expressions) {
+ return and(Arrays.asList(expressions));
+ }
+
+ public static Expression and(Collection<Expression> expressions) {
+ return logicalExpression(LogicalExpression.Operator.AND, expressions);
+ }
+
+ public static Expression or(Expression... expressions) {
+ return or(Arrays.asList(expressions));
+ }
+
+ public static Expression or(Collection<Expression> expressions) {
+ return logicalExpression(LogicalExpression.Operator.OR, expressions);
+ }
+
+ public static Expression logicalExpression(
+ LogicalExpression.Operator operator, Collection<Expression> expressions) {
+ requireNonNull(operator, "operator is null");
+ requireNonNull(expressions, "expressions is null");
+
+ if (expressions.isEmpty()) {
+ switch (operator) {
+ case AND:
+ return TRUE_LITERAL;
+ case OR:
+ return FALSE_LITERAL;
+ }
+ throw new IllegalArgumentException("Unsupported LogicalExpression operator");
+ }
+
+ if (expressions.size() == 1) {
+ return Iterables.getOnlyElement(expressions);
+ }
+
+ return new LogicalExpression(operator, ImmutableList.copyOf(expressions));
+ }
+
+ public static Expression combinePredicates(
+ LogicalExpression.Operator operator, Collection<Expression> expressions) {
+ if (operator == LogicalExpression.Operator.AND) {
+ return combineConjuncts(expressions);
+ }
+
+ return combineDisjuncts(expressions);
+ }
+
+ public static Expression combineConjuncts(Expression... expressions) {
+ return combineConjuncts(Arrays.asList(expressions));
+ }
+
+ public static Expression combineConjuncts(Collection<Expression> expressions) {
+ requireNonNull(expressions, "expressions is null");
+
+ List<Expression> conjuncts =
+ expressions.stream()
+ .flatMap(e -> extractConjuncts(e).stream())
+ .filter(e -> !e.equals(TRUE_LITERAL))
+ .collect(toList());
+
+ // TODO add removeDuplicates impl
+ // conjuncts = removeDuplicates(conjuncts);
+
+ if (conjuncts.contains(FALSE_LITERAL)) {
+ return FALSE_LITERAL;
+ }
+
+ return and(conjuncts);
+ }
+
+ public static Expression combineConjunctsWithDuplicates(Collection<Expression> expressions) {
+ requireNonNull(expressions, "expressions is null");
+
+ List<Expression> conjuncts =
+ expressions.stream()
+ .flatMap(e -> extractConjuncts(e).stream())
+ .filter(e -> !e.equals(TRUE_LITERAL))
+ .collect(toList());
+
+ if (conjuncts.contains(FALSE_LITERAL)) {
+ return FALSE_LITERAL;
+ }
+
+ return and(conjuncts);
+ }
+
+ public static Expression combineDisjuncts(Expression... expressions) {
+ return combineDisjuncts(Arrays.asList(expressions));
+ }
+
+ public static Expression combineDisjuncts(Collection<Expression> expressions) {
+ return combineDisjunctsWithDefault(expressions, FALSE_LITERAL);
+ }
+
+ public static Expression combineDisjunctsWithDefault(
+ Collection<Expression> expressions, Expression emptyDefault) {
+ requireNonNull(expressions, "expressions is null");
+
+ List<Expression> disjuncts =
+ expressions.stream()
+ .flatMap(e -> extractDisjuncts(e).stream())
+ .filter(e -> !e.equals(FALSE_LITERAL))
+ .collect(toList());
+
+ // TODO add removeDuplicates impl
+ // disjuncts = removeDuplicates(disjuncts);
+
+ if (disjuncts.contains(TRUE_LITERAL)) {
+ return TRUE_LITERAL;
+ }
+
+ return disjuncts.isEmpty() ? emptyDefault : or(disjuncts);
+ }
+
+ // public static Expression filterDeterministicConjuncts(Metadata metadata, Expression
+ // expression)
+ // {
+ // return filterConjuncts(expression, expression1 ->
+ // DeterminismEvaluator.isDeterministic(expression1));
+ // }
+ //
+ // public static Expression filterNonDeterministicConjuncts(Metadata metadata, Expression
+ // expression)
+ // {
+ // return filterConjuncts(expression, not(testExpression ->
+ // DeterminismEvaluator.isDeterministic(testExpression)));
+ // }
+
+ public static Expression filterConjuncts(Expression expression, Predicate<Expression> predicate) {
+ List<Expression> conjuncts =
+ extractConjuncts(expression).stream().filter(predicate).collect(toList());
+
+ return combineConjuncts(conjuncts);
+ }
+
+ // @SafeVarargs
+ // public static Function<Expression, Expression> expressionOrNullSymbols(Predicate<Symbol>...
+ // nullSymbolScopes)
+ // {
+ // return expression -> {
+ // ImmutableList.Builder<Expression> resultDisjunct = ImmutableList.builder();
+ // resultDisjunct.add(expression);
+ //
+ // for (Predicate<Symbol> nullSymbolScope : nullSymbolScopes) {
+ // List<Symbol> symbols = SymbolsExtractor.extractUnique(expression).stream()
+ // .filter(nullSymbolScope)
+ // .collect(toImmutableList());
+ //
+ // if (symbols.isEmpty()) {
+ // continue;
+ // }
+ //
+ // ImmutableList.Builder<Expression> nullConjuncts = ImmutableList.builder();
+ // for (Symbol symbol : symbols) {
+ // nullConjuncts.add(new IsNullPredicate(symbol.toSymbolReference()));
+ // }
+ //
+ // resultDisjunct.add(and(nullConjuncts.build()));
+ // }
+ //
+ // return or(resultDisjunct.build());
+ // };
+ // }
+
+ /**
+ * Removes duplicate deterministic expressions. Preserves the relative order of the expressions in
+ * the list.
+ */
+ // private static List<Expression> removeDuplicates(List<Expression> expressions)
+ // {
+ // Set<Expression> seen = new HashSet<>();
+ //
+ // ImmutableList.Builder<Expression> result = ImmutableList.builder();
+ // for (Expression expression : expressions) {
+ // if (!DeterminismEvaluator.isDeterministic(expression)) {
+ // result.add(expression);
+ // }
+ // else if (!seen.contains(expression)) {
+ // result.add(expression);
+ // seen.add(expression);
+ // }
+ // }
+ //
+ // return result.build();
+ // }
+
+ // public static Stream<Expression> preOrder(Expression node)
+ // {
+ // return stream(
+ // Traverser.forTree((SuccessorsFunction<Expression>) Expression::getChildren)
+ // .depthFirstPreOrder(requireNonNull(node, "node is null")));
+ // }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrVisitor.java
new file mode 100644
index 00000000000..96bc02b17e0
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrVisitor.java
@@ -0,0 +1,136 @@
+/*
+ * 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.plan.relational.planner.ir;
+
+import org.apache.iotdb.db.relational.sql.tree.ArithmeticBinaryExpression;
+import org.apache.iotdb.db.relational.sql.tree.AstVisitor;
+import org.apache.iotdb.db.relational.sql.tree.BetweenPredicate;
+import org.apache.iotdb.db.relational.sql.tree.Cast;
+import org.apache.iotdb.db.relational.sql.tree.CoalesceExpression;
+import org.apache.iotdb.db.relational.sql.tree.ComparisonExpression;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.relational.sql.tree.InPredicate;
+import org.apache.iotdb.db.relational.sql.tree.IsNullPredicate;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+import org.apache.iotdb.db.relational.sql.tree.NotExpression;
+import org.apache.iotdb.db.relational.sql.tree.NullIfExpression;
+import org.apache.iotdb.db.relational.sql.tree.Row;
+import org.apache.iotdb.db.relational.sql.tree.SearchedCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SimpleCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.SymbolReference;
+
+public abstract class IrVisitor<R, C> extends AstVisitor<R, C> {
+ public R process(Expression node) {
+ return process(node, null);
+ }
+
+ public R process(Expression node, C context) {
+ return node.accept(this, context);
+ }
+
+ protected R visitExpression(Expression node, C context) {
+ return null;
+ }
+
+ protected R visitArithmeticBinary(ArithmeticBinaryExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitBetweenPredicate(BetweenPredicate node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitCoalesceExpression(CoalesceExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitComparisonExpression(ComparisonExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ // protected R visitConstant(Constant node, C context)
+ // {
+ // return visitExpression(node, context);
+ // }
+
+ protected R visitInPredicate(InPredicate node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitFunctionCall(FunctionCall node, C context) {
+ return visitExpression(node, context);
+ }
+
+ // protected R visitLambdaExpression(LambdaExpression node, C context)
+ // {
+ // return visitExpression(node, context);
+ // }
+
+ protected R visitSimpleCaseExpression(SimpleCaseExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitNullIfExpression(NullIfExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ // protected R visitArithmeticNegation(ArithmeticNegation node, C context)
+ // {
+ // return visitExpression(node, context);
+ // }
+
+ protected R visitNotExpression(NotExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitSearchedCaseExpression(SearchedCaseExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitIsNullPredicate(IsNullPredicate node, C context) {
+ return visitExpression(node, context);
+ }
+
+ // protected R visitSubscriptExpression(SubscriptExpression node, C context)
+ // {
+ // return visitExpression(node, context);
+ // }
+
+ protected R visitLogicalExpression(LogicalExpression node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitRow(Row node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitCast(Cast node, C context) {
+ return visitExpression(node, context);
+ }
+
+ protected R visitSymbolReference(SymbolReference node, C context) {
+ return visitExpression(node, context);
+ }
+
+ // protected R visitBindExpression(BindExpression node, C context)
+ // {
+ // return visitExpression(node, context);
+ // }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java
new file mode 100644
index 00000000000..314da530323
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java
@@ -0,0 +1,136 @@
+/*
+ * 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.plan.relational.planner.ir;
+
+import org.apache.iotdb.db.relational.sql.tree.ComparisonExpression;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.InPredicate;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.and;
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.or;
+import static org.apache.iotdb.db.relational.sql.tree.ComparisonExpression.Operator.EQUAL;
+import static org.apache.iotdb.db.relational.sql.tree.LogicalExpression.Operator.AND;
+
+/** Transfer to conjunction normal form. */
+public final class NormalizeOrExpressionRewriter {
+ public static Expression normalizeOrExpression(Expression expression) {
+ return ExpressionTreeRewriter.rewriteWith(new Visitor(), expression);
+ }
+
+ private NormalizeOrExpressionRewriter() {}
+
+ private static class Visitor extends ExpressionRewriter<Void> {
+ @Override
+ public Expression rewriteLogicalExpression(
+ LogicalExpression node, Void context, ExpressionTreeRewriter<Void> treeRewriter) {
+ List<Expression> terms =
+ node.getTerms().stream()
+ .map(expression -> treeRewriter.rewrite(expression, context))
+ .collect(toImmutableList());
+
+ if (node.getOperator() == AND) {
+ return and(terms);
+ }
+
+ ImmutableList.Builder<InPredicate> inPredicateBuilder = ImmutableList.builder();
+ ImmutableSet.Builder<Expression> expressionToSkipBuilder = ImmutableSet.builder();
+ ImmutableList.Builder<Expression> othersExpressionBuilder = ImmutableList.builder();
+ groupComparisonAndInPredicate(terms)
+ .forEach(
+ (expression, values) -> {
+ if (values.size() > 1) {
+ // TODO mergeToInListExpression may have more than one value
+ inPredicateBuilder.add(
+ new InPredicate(expression, mergeToInListExpression(values).get(0)));
+ expressionToSkipBuilder.add(expression);
+ }
+ });
+
+ Set<Expression> expressionToSkip = expressionToSkipBuilder.build();
+ for (Expression expression : terms) {
+ if (expression instanceof ComparisonExpression
+ && ((ComparisonExpression) expression).getOperator() == EQUAL) {
+
+ if (!expressionToSkip.contains(((ComparisonExpression) expression).getLeft())) {
+ othersExpressionBuilder.add(expression);
+ }
+ } else if (expression instanceof InPredicate) {
+ if (!expressionToSkip.contains(((InPredicate) expression).getValue())) {
+ othersExpressionBuilder.add(expression);
+ }
+ } else {
+ othersExpressionBuilder.add(expression);
+ }
+ }
+
+ return or(
+ ImmutableList.<Expression>builder()
+ .addAll(othersExpressionBuilder.build())
+ .addAll(inPredicateBuilder.build())
+ .build());
+ }
+
+ private List<Expression> mergeToInListExpression(Collection<Expression> expressions) {
+ LinkedHashSet<Expression> expressionValues = new LinkedHashSet<>();
+ for (Expression expression : expressions) {
+ if (expression instanceof ComparisonExpression
+ && ((ComparisonExpression) expression).getOperator() == EQUAL) {
+ expressionValues.add(((ComparisonExpression) expression).getRight());
+ } else if (expression instanceof InPredicate) {
+ // TODO inPredicate has getValues method
+ expressionValues.add(((InPredicate) expression).getValue());
+ } else {
+ throw new IllegalStateException("Unexpected expression: " + expression);
+ }
+ }
+
+ return ImmutableList.copyOf(expressionValues);
+ }
+
+ private Map<Expression, Collection<Expression>> groupComparisonAndInPredicate(
+ List<Expression> terms) {
+ ImmutableMultimap.Builder<Expression, Expression> expressionBuilder =
+ ImmutableMultimap.builder();
+ for (Expression expression : terms) {
+ if (expression instanceof ComparisonExpression
+ && ((ComparisonExpression) expression).getOperator() == EQUAL) {
+ expressionBuilder.put(((ComparisonExpression) expression).getLeft(), expression);
+ } else if (expression instanceof InPredicate) {
+ InPredicate inPredicate = (InPredicate) expression;
+ expressionBuilder.put(inPredicate.getValue(), inPredicate);
+ }
+ }
+
+ return expressionBuilder.build().asMap();
+ }
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
index e56946216fc..3c49baa4880 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
@@ -1,7 +1,27 @@
+/*
+ * 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.plan.relational.planner.node;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
import org.apache.iotdb.db.relational.sql.tree.Expression;
@@ -11,13 +31,18 @@ import java.nio.ByteBuffer;
import java.util.List;
public class FilterNode extends SingleChildProcessNode {
- private final Expression predicate;
+ private Expression predicate;
public FilterNode(PlanNodeId id, PlanNode child, Expression predicate) {
super(id, child);
this.predicate = predicate;
}
+ @Override
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitFilter(this, context);
+ }
+
@Override
public PlanNode clone() {
return null;
@@ -33,4 +58,12 @@ public class FilterNode extends SingleChildProcessNode {
@Override
protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+
+ public Expression getPredicate() {
+ return predicate;
+ }
+
+ public void setPredicate(Expression predicate) {
+ this.predicate = predicate;
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/MergeSortNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/MergeSortNode.java
new file mode 100644
index 00000000000..58a94fc3b9c
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/MergeSortNode.java
@@ -0,0 +1,58 @@
+/*
+ * 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.plan.relational.planner.node;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.MultiChildProcessNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.OrderByParameter;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class MergeSortNode extends MultiChildProcessNode {
+ private final OrderByParameter mergeOrderParameter;
+
+ private final List<String> outputColumns;
+
+ public MergeSortNode(
+ PlanNodeId id, OrderByParameter mergeOrderParameter, List<String> outputColumns) {
+ super(id);
+ this.mergeOrderParameter = mergeOrderParameter;
+ this.outputColumns = outputColumns;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
index 860bbbab443..1b7c2476ba3 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
@@ -2,6 +2,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
@@ -28,6 +29,11 @@ public class TableScanNode extends PlanNode {
this.assignments = assignments;
}
+ @Override
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitTableScan(this, context);
+ }
+
@Override
public List<PlanNode> getChildren() {
return null;
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TopKNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TopKNode.java
new file mode 100644
index 00000000000..d356d25f1d0
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TopKNode.java
@@ -0,0 +1,59 @@
+/*
+ * 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.plan.relational.planner.node;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.MultiChildProcessNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.OrderingScheme;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class TopKNode extends MultiChildProcessNode {
+
+ private final OrderingScheme orderingScheme;
+
+ private final long count;
+
+ public TopKNode(PlanNodeId id, OrderingScheme scheme, long count) {
+ super(id);
+ this.orderingScheme = scheme;
+ this.count = count;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RelationalPlanOptimizer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RelationalPlanOptimizer.java
new file mode 100644
index 00000000000..df7cb945167
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RelationalPlanOptimizer.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed 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.plan.relational.planner.optimizations;
+
+import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
+
+public interface RelationalPlanOptimizer {
+ PlanNode optimize(PlanNode planNode, Analysis analysis, MPPQueryContext context);
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SimplifyExpressions.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SimplifyExpressions.java
new file mode 100644
index 00000000000..e9932be033f
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SimplifyExpressions.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed 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.plan.relational.planner.optimizations;
+
+import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
+import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.NormalizeOrExpressionRewriter.normalizeOrExpression;
+
+public class SimplifyExpressions implements RelationalPlanOptimizer {
+
+ @Override
+ public PlanNode optimize(PlanNode planNode, Analysis analysis, MPPQueryContext context) {
+ // TODO add query statement pruning
+ return planNode.accept(new Rewriter(), new RewriterContext());
+ }
+
+ private static class Rewriter extends PlanVisitor<PlanNode, RewriterContext> {
+
+ @Override
+ public PlanNode visitPlan(PlanNode node, RewriterContext context) {
+ // PlanNode newNode = node.clone();
+ if (node.getChildren() == null) {
+ System.out.println("aa");
+ }
+ for (PlanNode child : node.getChildren()) {
+ child.accept(this, context);
+ }
+ return node;
+ }
+
+ @Override
+ public PlanNode visitFilter(FilterNode node, RewriterContext context) {
+ Expression newPredicate = normalizeOrExpression(node.getPredicate());
+ node.setPredicate(newPredicate);
+ return node;
+ }
+
+ @Override
+ public PlanNode visitTableScan(TableScanNode node, RewriterContext context) {
+ return node;
+ }
+ }
+
+ private static class RewriterContext {}
+}
diff --git a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
index 98aee549f2e..29ab6b83674 100644
--- a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
+++ b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
@@ -31,7 +31,7 @@ public abstract class Expression extends Node {
/** Accessible for {@link AstVisitor}, use {@link AstVisitor#process(Node, Object)} instead. */
@Override
- protected <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+ public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
return visitor.visitExpression(this, context);
}
diff --git a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericDataType.java b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericDataType.java
index 99059ba7c5f..f5c39eae80e 100644
--- a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericDataType.java
+++ b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericDataType.java
@@ -60,7 +60,7 @@ public final class GenericDataType extends DataType {
}
@Override
- protected <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+ public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
return visitor.visitGenericDataType(this, context);
}