This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch ty/TableModelGrammar
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/ty/TableModelGrammar by this
push:
new 5f41dfbd5e6 Make globalTableTimePredicate valid, add
PruneUnUsedColumns and PredicatePushDown optimize rules
5f41dfbd5e6 is described below
commit 5f41dfbd5e60c9b8c7a99ae9e9d098889ecfe2b6
Author: Beyyes <[email protected]>
AuthorDate: Wed Jun 5 17:40:28 2024 +0800
Make globalTableTimePredicate valid, add PruneUnUsedColumns and
PredicatePushDown optimize rules
---
.../db/queryengine/common/MPPQueryContext.java | 6 +
.../plan/planner/plan/TableModelTimePredicate.java | 5 +-
.../plan/planner/plan/TimePredicate.java | 12 +-
.../plan/planner/plan/TreeModelTimePredicate.java | 2 +
.../plan/planner/plan/node/PlanVisitor.java | 4 +-
.../plan/relational/analyzer/Analysis.java | 11 +
.../relational/metadata/TableMetadataImpl.java | 4 +
.../plan/relational/planner/Assignments.java | 19 +-
.../plan/relational/planner/LogicalPlanner.java | 6 +-
.../plan/relational/planner/QueryPlanner.java | 5 +
.../relational/planner/node/TableScanNode.java | 32 ++-
.../planner/optimizations/IndexScan.java | 29 ++-
...ableScanColumns.java => PredicatePushDown.java} | 85 +++++--
...bleScanColumns.java => PruneUnUsedColumns.java} | 57 ++++-
.../RemoveRedundantIdentityProjections.java | 1 +
.../plan/relational/sql/tree/DataType.java | 1 -
.../plan/relational/analyzer/AnalyzerTest.java | 278 ++++++++++++++++++---
.../plan/relational/analyzer/TestMatadata.java | 118 +--------
18 files changed, 469 insertions(+), 206 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 3a42ee805b0..988224da729 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
@@ -218,11 +218,17 @@ public class MPPQueryContext {
}
}
+ // used for tree model
public void generateGlobalTimeFilter(Analysis analysis) {
this.globalTimeFilter =
PredicateUtils.convertPredicateToTimeFilter(analysis.getGlobalTimePredicate());
}
+ // used for table model
+ public void setGlobalTimeFilter(Filter globalTimeFilter) {
+ this.globalTimeFilter = globalTimeFilter;
+ }
+
public Filter getGlobalTimeFilter() {
// time filter may be stateful, so we need to copy it
return globalTimeFilter != null ? globalTimeFilter.copy() : null;
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TableModelTimePredicate.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TableModelTimePredicate.java
index b4d1337b16e..fa7a82f484e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TableModelTimePredicate.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TableModelTimePredicate.java
@@ -23,10 +23,10 @@ import
org.apache.iotdb.db.queryengine.plan.analyze.PredicateUtils;
import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Expression;
import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.util.Objects;
public class TableModelTimePredicate implements TimePredicate {
@@ -39,7 +39,8 @@ public class TableModelTimePredicate implements TimePredicate
{
@Override
public void serialize(DataOutputStream stream) throws IOException {
- throw new UnsupportedEncodingException();
+ ReadWriteIOUtils.write((byte) 1, stream);
+ Expression.serialize(timePredicate, stream);
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TimePredicate.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TimePredicate.java
index 04cf67ed3aa..348fa1bfe5e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TimePredicate.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TimePredicate.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.queryengine.plan.planner.plan;
import org.apache.iotdb.db.queryengine.plan.expression.Expression;
import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -34,7 +35,14 @@ public interface TimePredicate {
Filter convertPredicateToTimeFilter();
static TimePredicate deserialize(ByteBuffer byteBuffer) {
- // TODO will return another kind of TimePredicate like
TableModelTimePredicate in the future
- return new TreeModelTimePredicate(Expression.deserialize(byteBuffer));
+ // 0 for tree model, 1 for table model
+ byte type = ReadWriteIOUtils.readByte(byteBuffer);
+ if (type == 0) {
+ return new TreeModelTimePredicate(Expression.deserialize(byteBuffer));
+ } else {
+ return new TableModelTimePredicate(
+
org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Expression.deserialize(
+ byteBuffer));
+ }
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TreeModelTimePredicate.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TreeModelTimePredicate.java
index 5b9e90cd8d0..5cc00125aa7 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TreeModelTimePredicate.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/TreeModelTimePredicate.java
@@ -23,6 +23,7 @@ import
org.apache.iotdb.db.queryengine.plan.analyze.PredicateUtils;
import org.apache.iotdb.db.queryengine.plan.expression.Expression;
import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -38,6 +39,7 @@ public class TreeModelTimePredicate implements TimePredicate {
@Override
public void serialize(DataOutputStream stream) throws IOException {
+ ReadWriteIOUtils.write((byte) 0, stream);
Expression.serialize(timePredicate, stream);
}
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 85cc1a83625..2e5a61b7a83 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
@@ -548,7 +548,7 @@ public abstract class PlanVisitor<R, C> {
public R visitProject(
org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode
node, C context) {
- return visitPlan(node, context);
+ return visitSingleChildProcess(node, context);
}
public R visitLimit(
@@ -567,7 +567,7 @@ public abstract class PlanVisitor<R, C> {
public R visitOutput(
org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode
node, C context) {
- return visitPlan(node, context);
+ return visitSingleChildProcess(node, context);
}
public R visitSort(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
index adcc56e464b..a74f35526b5 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
@@ -158,6 +158,9 @@ public class Analysis implements IAnalysis {
private boolean finishQueryAfterAnalyze;
+ // indicate is there a value filter
+ private boolean hasValueFilter = false;
+
public Expression getGlobalTableModelTimePredicate() {
return this.globalTableModelTimePredicate;
}
@@ -567,6 +570,14 @@ public class Analysis implements IAnalysis {
return aliasedRelations.contains(NodeRef.of(relation));
}
+ public boolean hasValueFilter() {
+ return hasValueFilter;
+ }
+
+ public void setHasValueFilter(boolean hasValueFilter) {
+ this.hasValueFilter = hasValueFilter;
+ }
+
@Override
public boolean isFailed() {
return false;
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
index 8d622c8dc02..7859fc64186 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
@@ -106,6 +106,10 @@ public class TableMetadataImpl implements Metadata {
@Override
public Type getFunctionReturnType(String functionName, List<? extends Type>
argumentTypes) {
+ return getFunctionType(functionName, argumentTypes);
+ }
+
+ public static Type getFunctionType(String functionName, List<? extends Type>
argumentTypes) {
// builtin scalar function
if
(BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName)) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
index ffb2b62c521..5bc8a178c11 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
@@ -16,7 +16,6 @@ package
org.apache.iotdb.db.queryengine.plan.relational.planner;
import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Expression;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Collection;
@@ -68,7 +67,7 @@ public class Assignments {
}
public Assignments(Map<Symbol, Expression> assignments) {
- this.assignments = ImmutableMap.copyOf(requireNonNull(assignments,
"assignments is null"));
+ this.assignments = requireNonNull(assignments, "assignments is null");
}
public List<Symbol> getOutputs() {
@@ -95,6 +94,18 @@ public class Assignments {
.collect(toAssignments());
}
+ public boolean contains(Symbol symbol) {
+ return assignments.containsKey(symbol);
+ }
+
+ public Expression get(Symbol symbol) {
+ return assignments.get(symbol);
+ }
+
+ public void remove(Symbol symbol) {
+ this.assignments.remove(symbol);
+ }
+
private Collector<Entry<Symbol, Expression>, Builder, Assignments>
toAssignments() {
return Collector.of(
Assignments::builder,
@@ -118,10 +129,6 @@ public class Assignments {
return assignments.entrySet();
}
- public Expression get(Symbol symbol) {
- return assignments.get(symbol);
- }
-
public int size() {
return assignments.size();
}
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 fb593d4c852..dfc2bfae5a2 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
@@ -29,7 +29,8 @@ 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.IndexScan;
-import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PruneTableScanColumns;
+import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PredicatePushDown;
+import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PruneUnUsedColumns;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.RelationalPlanOptimizer;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.RemoveRedundantIdentityProjections;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SimplifyExpressions;
@@ -77,8 +78,9 @@ public class LogicalPlanner {
this.relationalPlanOptimizers =
Arrays.asList(
new SimplifyExpressions(),
+ new PruneUnUsedColumns(),
+ new PredicatePushDown(),
new RemoveRedundantIdentityProjections(),
- new PruneTableScanColumns(),
new IndexScan());
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
index a3e1dec9819..0130b13af47 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
@@ -19,6 +19,7 @@ import org.apache.iotdb.db.queryengine.common.SessionInfo;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
+import
org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.ConvertPredicateToTimeFilterVisitor;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTranslateVisitor;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.LimitNode;
@@ -250,12 +251,16 @@ public class QueryPlanner {
if (resultPair.left != null) {
globalTimePredicate =
ExpressionTranslateVisitor.translateToSymbolReference(resultPair.left,
planBuilder);
+
+ queryContext.setGlobalTimeFilter(
+ globalTimePredicate.accept(new
ConvertPredicateToTimeFilterVisitor(), null));
}
analysis.setGlobalTableModelTimePredicate(globalTimePredicate);
boolean hasValueFilter = resultPair.right;
if (!hasValueFilter) {
return planBuilder;
}
+ analysis.setHasValueFilter(true);
// TODO if predicate equals TrueConstant, no need filter
return planBuilder.withNewRoot(
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 33dfa3e04bc..6fd46d63678 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
@@ -45,8 +45,8 @@ public class TableScanNode extends SourceNode {
// db.tablename
private final String qualifiedTableName;
- private final List<Symbol> outputSymbols;
- private final Map<Symbol, ColumnSchema> assignments;
+ private List<Symbol> outputSymbols;
+ private Map<Symbol, ColumnSchema> assignments;
private List<DeviceEntry> deviceEntries;
private Map<Symbol, Integer> idAndAttributeIndexMap;
@@ -276,6 +276,18 @@ public class TableScanNode extends SourceNode {
return this.qualifiedTableName;
}
+ public void setOutputSymbols(List<Symbol> outputSymbols) {
+ this.outputSymbols = outputSymbols;
+ }
+
+ public void setAssignments(Map<Symbol, ColumnSchema> assignments) {
+ this.assignments = assignments;
+ }
+
+ public Map<Symbol, ColumnSchema> getAssignments() {
+ return this.assignments;
+ }
+
public void setDeviceEntries(List<DeviceEntry> deviceEntries) {
this.deviceEntries = deviceEntries;
}
@@ -288,10 +300,6 @@ public class TableScanNode extends SourceNode {
this.idAndAttributeIndexMap = idAndAttributeIndexMap;
}
- public Map<Symbol, ColumnSchema> getAssignments() {
- return this.assignments;
- }
-
public Ordering getScanOrder() {
return this.scanOrder;
}
@@ -300,10 +308,6 @@ public class TableScanNode extends SourceNode {
return deviceEntries;
}
- public Expression getPushDownPredicate() {
- return this.pushDownPredicate;
- }
-
public long getPushDownLimit() {
return this.pushDownLimit;
}
@@ -312,6 +316,14 @@ public class TableScanNode extends SourceNode {
return this.pushDownOffset;
}
+ public Expression getPushDownPredicate() {
+ return this.pushDownPredicate;
+ }
+
+ public void setPushDownPredicate(@Nullable Expression pushDownPredicate) {
+ this.pushDownPredicate = pushDownPredicate;
+ }
+
public boolean isPushLimitToEachDevice() {
return pushLimitToEachDevice;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/IndexScan.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/IndexScan.java
index 56eb8ea082d..fd3fe00bd9f 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/IndexScan.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/IndexScan.java
@@ -58,8 +58,6 @@ public class IndexScan implements RelationalPlanOptimizer {
static final IoTDBConfig CONFIG = IoTDBDescriptor.getInstance().getConfig();
- private MPPQueryContext mppQueryContext;
-
@Override
public PlanNode optimize(
PlanNode planNode,
@@ -67,14 +65,15 @@ public class IndexScan implements RelationalPlanOptimizer {
Metadata metadata,
IPartitionFetcher partitionFetcher,
SessionInfo sessionInfo,
- MPPQueryContext context) {
+ MPPQueryContext queryContext) {
return planNode.accept(
new Rewriter(),
- new RewriterContext(null, metadata, sessionInfo, analysis,
partitionFetcher));
+ new RewriterContext(null, metadata, sessionInfo, analysis,
partitionFetcher, queryContext));
}
private static class Rewriter extends PlanVisitor<PlanNode, RewriterContext>
{
+
@Override
public PlanNode visitPlan(PlanNode node, RewriterContext context) {
for (PlanNode child : node.getChildren()) {
@@ -109,9 +108,7 @@ public class IndexScan implements RelationalPlanOptimizer {
attributeColumns);
node.setDeviceEntries(deviceEntries);
- // TODO getDataPartition, Change globalTimeFilter to Filter
- String treeDatabase = "root." + dbName;
- Filter globalTimeFilter = null;
+ String treeModelDatabase = "root." + dbName;
Set<String> deviceSet = new HashSet<>();
for (DeviceEntry deviceEntry : deviceEntries) {
StringArrayDeviceID arrayDeviceID = (StringArrayDeviceID)
deviceEntry.getDeviceID();
@@ -121,7 +118,10 @@ public class IndexScan implements RelationalPlanOptimizer {
DataPartition dataPartition =
fetchDataPartitionByDevices(
- deviceSet, treeDatabase, globalTimeFilter,
context.partitionFetcher);
+ deviceSet,
+ treeModelDatabase,
+ context.getQueryContext().getGlobalTimeFilter(),
+ context.getPartitionFetcher());
context.getAnalysis().setDataPartition(dataPartition);
if (dataPartition.getDataPartitionMap().size() > 1) {
@@ -218,18 +218,21 @@ public class IndexScan implements RelationalPlanOptimizer
{
private final SessionInfo sessionInfo;
private final Analysis analysis;
private final IPartitionFetcher partitionFetcher;
+ private final MPPQueryContext queryContext;
RewriterContext(
Expression predicate,
Metadata metadata,
SessionInfo sessionInfo,
Analysis analysis,
- IPartitionFetcher partitionFetcher) {
+ IPartitionFetcher partitionFetcher,
+ MPPQueryContext queryContext) {
this.predicate = predicate;
this.metadata = metadata;
this.sessionInfo = sessionInfo;
this.analysis = analysis;
this.partitionFetcher = partitionFetcher;
+ this.queryContext = queryContext;
}
public Expression getPredicate() {
@@ -255,5 +258,13 @@ public class IndexScan implements RelationalPlanOptimizer {
public Analysis getAnalysis() {
return this.analysis;
}
+
+ public IPartitionFetcher getPartitionFetcher() {
+ return partitionFetcher;
+ }
+
+ public MPPQueryContext getQueryContext() {
+ return queryContext;
+ }
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PredicatePushDown.java
similarity index 54%
copy from
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
copy to
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PredicatePushDown.java
index cbc18a51465..ae05611ce08 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PredicatePushDown.java
@@ -19,22 +19,22 @@ import org.apache.iotdb.db.queryengine.common.SessionInfo;
import org.apache.iotdb.db.queryengine.plan.analyze.IPartitionFetcher;
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.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.analyzer.Analysis;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
-import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
-import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
-import
org.apache.iotdb.db.queryengine.plan.relational.sql.tree.DefaultTraversalVisitor;
-import
org.apache.iotdb.db.queryengine.plan.relational.sql.tree.SymbolReference;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Expression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Node;
-import com.google.common.collect.ImmutableList;
-
-import java.util.HashSet;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
-public class PruneTableScanColumns implements RelationalPlanOptimizer {
+/** Push down predicate to TableScanNode as possible. */
+public class PredicatePushDown implements RelationalPlanOptimizer {
+
@Override
public PlanNode optimize(
PlanNode planNode,
@@ -43,52 +43,83 @@ public class PruneTableScanColumns implements
RelationalPlanOptimizer {
IPartitionFetcher partitionFetcher,
SessionInfo sessionInfo,
MPPQueryContext context) {
+
+ if (!analysis.hasValueFilter()) {
+ return planNode;
+ }
+
return planNode.accept(new Rewriter(), new RewriterContext());
}
private static class Rewriter extends PlanVisitor<PlanNode, RewriterContext>
{
+
@Override
public PlanNode visitPlan(PlanNode node, RewriterContext context) {
- for (PlanNode child : node.getChildren()) {
- child.accept(this, context);
- }
+ throw new IllegalArgumentException(
+ String.format("Unexpected plan node: %s in TableModel
PredicatePushDown", node));
+ }
+
+ @Override
+ public PlanNode visitSingleChildProcess(SingleChildProcessNode node,
RewriterContext context) {
+ PlanNode rewrittenChild = node.getChild().accept(this, context);
+ node.setChild(rewrittenChild);
return node;
}
@Override
- public PlanNode visitProject(ProjectNode node, RewriterContext context) {
- context.symbolHashSet.addAll(node.getOutputSymbols());
- node.getChild().accept(this, context);
+ public PlanNode visitMultiChildProcess(MultiChildProcessNode node,
RewriterContext context) {
+ List<PlanNode> rewrittenChildren = new ArrayList<>();
+ for (PlanNode child : node.getChildren()) {
+ rewrittenChildren.add(child.accept(this, context));
+ }
+ node.setChildren(rewrittenChildren);
return node;
}
@Override
public PlanNode visitFilter(FilterNode node, RewriterContext context) {
- ImmutableList.Builder<Symbol> symbolBuilder = ImmutableList.builder();
- new SymbolBuilderVisitor().process(node.getPredicate(),
ImmutableList.builder());
- List<Symbol> ret = symbolBuilder.build();
- context.symbolHashSet.addAll(ret);
+ if (node.getPredicate() != null) {
+ // when exist diff function, predicate can not be pushed down
+ if (containsDiffFunction(node.getPredicate())) {
+ return node;
+ }
+
+ context.pushDownPredicate = node.getPredicate();
+ node.getChild().accept(this, context);
+
+ // remove FilterNode after push down
+ return node.getChild();
+ }
+
node.getChild().accept(this, context);
return node;
}
@Override
public PlanNode visitTableScan(TableScanNode node, RewriterContext
context) {
+ node.setPushDownPredicate(context.pushDownPredicate);
return node;
}
}
- private static class SymbolBuilderVisitor
- extends DefaultTraversalVisitor<ImmutableList.Builder<Symbol>> {
- @Override
- protected Void visitSymbolReference(
- SymbolReference node, ImmutableList.Builder<Symbol> builder) {
- builder.add(Symbol.from(node));
- return null;
+ static boolean containsDiffFunction(Expression expression) {
+ if (expression instanceof FunctionCall
+ && "diff".equalsIgnoreCase(((FunctionCall)
expression).getName().toString())) {
+ return true;
}
+
+ if (!expression.getChildren().isEmpty()) {
+ for (Node node : expression.getChildren()) {
+ if (containsDiffFunction((Expression) node)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
private static class RewriterContext {
- Set<Symbol> symbolHashSet = new HashSet<>();
+ Expression pushDownPredicate;
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneUnUsedColumns.java
similarity index 58%
rename from
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
rename to
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneUnUsedColumns.java
index cbc18a51465..d6310c37dc0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneTableScanColumns.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PruneUnUsedColumns.java
@@ -20,21 +20,37 @@ import
org.apache.iotdb.db.queryengine.plan.analyze.IPartitionFetcher;
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.metadata.ColumnSchema;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.tree.DefaultTraversalVisitor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.tree.Expression;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.tree.SymbolReference;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
-public class PruneTableScanColumns implements RelationalPlanOptimizer {
+import static org.apache.iotdb.commons.conf.IoTDBConstant.TIME;
+
+/**
+ * Remove unused columns in TableScanNode.
+ *
+ * <p>For example, The output columns of TableScanNode in `select * from
table1` query are `tag1,
+ * attr1, s1`, but the output columns of TableScanNode in `select s1 from
table1` query can only be
+ * `s1`.
+ */
+public class PruneUnUsedColumns implements RelationalPlanOptimizer {
+
@Override
public PlanNode optimize(
PlanNode planNode,
@@ -47,6 +63,7 @@ public class PruneTableScanColumns implements
RelationalPlanOptimizer {
}
private static class Rewriter extends PlanVisitor<PlanNode, RewriterContext>
{
+
@Override
public PlanNode visitPlan(PlanNode node, RewriterContext context) {
for (PlanNode child : node.getChildren()) {
@@ -55,9 +72,28 @@ public class PruneTableScanColumns implements
RelationalPlanOptimizer {
return node;
}
+ @Override
+ public PlanNode visitOutput(OutputNode node, RewriterContext context) {
+ context.allUsedSymbolSet.addAll(node.getOutputSymbols());
+ node.getChild().accept(this, context);
+ return node;
+ }
+
@Override
public PlanNode visitProject(ProjectNode node, RewriterContext context) {
- context.symbolHashSet.addAll(node.getOutputSymbols());
+ // There must exist OutputNode above ProjectNode
+ node.getAssignments()
+ .getMap()
+ .entrySet()
+ .removeIf(entry ->
!context.allUsedSymbolSet.contains(entry.getKey()));
+ Set<Symbol> usedSymbolSet = new HashSet<>();
+ for (Map.Entry<Symbol, Expression> entry :
node.getAssignments().getMap().entrySet()) {
+ ImmutableList.Builder<Symbol> symbolBuilder = ImmutableList.builder();
+ new SymbolBuilderVisitor().process(entry.getValue(), symbolBuilder);
+ usedSymbolSet.addAll(symbolBuilder.build());
+ }
+
+ context.allUsedSymbolSet.addAll(usedSymbolSet);
node.getChild().accept(this, context);
return node;
}
@@ -65,15 +101,24 @@ public class PruneTableScanColumns implements
RelationalPlanOptimizer {
@Override
public PlanNode visitFilter(FilterNode node, RewriterContext context) {
ImmutableList.Builder<Symbol> symbolBuilder = ImmutableList.builder();
- new SymbolBuilderVisitor().process(node.getPredicate(),
ImmutableList.builder());
- List<Symbol> ret = symbolBuilder.build();
- context.symbolHashSet.addAll(ret);
+ new SymbolBuilderVisitor().process(node.getPredicate(), symbolBuilder);
+ context.allUsedSymbolSet.addAll(symbolBuilder.build());
node.getChild().accept(this, context);
return node;
}
@Override
public PlanNode visitTableScan(TableScanNode node, RewriterContext
context) {
+ List<Symbol> newOutputSymbols = new ArrayList<>();
+ Map<Symbol, ColumnSchema> newAssignments = new HashMap<>();
+ for (Symbol symbol : node.getOutputSymbols()) {
+ if (TIME.equalsIgnoreCase(symbol.getName()) ||
context.allUsedSymbolSet.contains(symbol)) {
+ newOutputSymbols.add(symbol);
+ newAssignments.put(symbol, node.getAssignments().get(symbol));
+ }
+ }
+ node.setOutputSymbols(newOutputSymbols);
+ node.setAssignments(newAssignments);
return node;
}
}
@@ -89,6 +134,6 @@ public class PruneTableScanColumns implements
RelationalPlanOptimizer {
}
private static class RewriterContext {
- Set<Symbol> symbolHashSet = new HashSet<>();
+ Set<Symbol> allUsedSymbolSet = new HashSet<>();
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RemoveRedundantIdentityProjections.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RemoveRedundantIdentityProjections.java
index 28b5ab8d1d7..474a327292f 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RemoveRedundantIdentityProjections.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/RemoveRedundantIdentityProjections.java
@@ -42,6 +42,7 @@ public class RemoveRedundantIdentityProjections implements
RelationalPlanOptimiz
}
private static class Rewriter extends PlanVisitor<PlanNode, RewriterContext>
{
+
@Override
public PlanNode visitPlan(PlanNode node, RewriterContext context) {
PlanNode newNode = node.clone();
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/tree/DataType.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/tree/DataType.java
index 7123c457ae7..d6abeb4f5d4 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/tree/DataType.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/tree/DataType.java
@@ -22,7 +22,6 @@ package
org.apache.iotdb.db.queryengine.plan.relational.sql.tree;
import javax.annotation.Nullable;
public abstract class DataType extends Expression {
-
DataType(@Nullable NodeLocation location) {
super(location);
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
index 104cdc9ed48..a2a3b56fc8d 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
@@ -45,7 +45,9 @@ import
org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableSchema;
import org.apache.iotdb.db.queryengine.plan.relational.planner.LogicalPlanner;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributionPlanner;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
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.TableScanNode;
import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl;
import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser;
@@ -79,6 +81,26 @@ public class AnalyzerTest {
private static final NopAccessControl nopAccessControl = new
NopAccessControl();
+ QueryId queryId = new QueryId("tmp_query");
+ SessionInfo sessionInfo =
+ new SessionInfo(
+ 1L,
+ "iotdb-user",
+ ZoneId.systemDefault(),
+ IoTDBConstant.ClientVersion.V_1_0,
+ "db",
+ IClientSession.SqlDialect.TABLE);
+ Metadata metadata = new TestMatadata();
+ String sql;
+ Analysis actualAnalysis;
+ MPPQueryContext context;
+ LogicalPlanner logicalPlanner;
+ LogicalQueryPlan logicalQueryPlan;
+ PlanNode rootNode;
+ TableDistributionPlanner distributionPlanner;
+ DistributedQueryPlan distributedQueryPlan;
+ TableScanNode tableScanNode;
+
@Test
public void testMockQuery() throws OperatorNotFoundException {
String sql =
@@ -145,52 +167,45 @@ public class AnalyzerTest {
System.out.println(actualAnalysis.getTypes());
}
- QueryId queryId = new QueryId("tmp_query");
- SessionInfo sessionInfo =
- new SessionInfo(
- 1L,
- "iotdb-user",
- ZoneId.systemDefault(),
- IoTDBConstant.ClientVersion.V_1_0,
- "db",
- IClientSession.SqlDialect.TABLE);
- Metadata metadata = new TestMatadata();
-
@Test
public void singleTableNoFilterTest() throws IoTDBException {
- // 1. wildcard
- String sql = "SELECT * FROM table1";
- Analysis actualAnalysis = analyzeSQL(sql, metadata);
+ // wildcard
+ sql = "SELECT * FROM table1";
+ actualAnalysis = analyzeSQL(sql, metadata);
assertNotNull(actualAnalysis);
assertEquals(1, actualAnalysis.getTables().size());
- MPPQueryContext context = new MPPQueryContext(sql, queryId, sessionInfo,
null, null);
- LogicalPlanner logicalPlanner =
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
new LogicalPlanner(
context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
- LogicalQueryPlan logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
- PlanNode rootNode = logicalQueryPlan.getRootNode();
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
assertTrue(rootNode instanceof OutputNode);
assertTrue(((OutputNode) rootNode).getChild() instanceof TableScanNode);
- TableScanNode tableScanNode = (TableScanNode) ((OutputNode)
rootNode).getChild();
+ tableScanNode = (TableScanNode) ((OutputNode) rootNode).getChild();
assertEquals("table1", tableScanNode.getQualifiedTableName());
- assertEquals(8, tableScanNode.getOutputSymbols().size());
- assertEquals(8, tableScanNode.getAssignments().size());
+ assertEquals(
+ Arrays.asList("time", "tag1", "tag2", "tag3", "attr1", "attr2", "s1",
"s2", "s3"),
+ tableScanNode.getOutputColumnNames());
+ assertEquals(9, tableScanNode.getOutputSymbols().size());
+ assertEquals(9, tableScanNode.getAssignments().size());
assertEquals(1, tableScanNode.getDeviceEntries().size());
assertEquals(5, tableScanNode.getIdAndAttributeIndexMap().size());
assertEquals(ASC, tableScanNode.getScanOrder());
- TableDistributionPlanner distributionPlanner =
- new TableDistributionPlanner(actualAnalysis, logicalQueryPlan,
context);
+ distributionPlanner = new TableDistributionPlanner(actualAnalysis,
logicalQueryPlan, context);
DistributedQueryPlan distributedQueryPlan = distributionPlanner.plan();
assertEquals(4, distributedQueryPlan.getInstances().size());
+ }
- // 2. global time filter
+ @Test
+ public void singleTableWithFilterTest() throws IoTDBException {
+ // global time filter
sql = "SELECT * FROM table1 where time > 1";
actualAnalysis = analyzeSQL(sql, metadata);
assertNotNull(actualAnalysis);
assertEquals(1, actualAnalysis.getTables().size());
-
context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
logicalPlanner =
new LogicalPlanner(
@@ -201,15 +216,222 @@ public class AnalyzerTest {
assertTrue(((OutputNode) rootNode).getChild() instanceof TableScanNode);
tableScanNode = (TableScanNode) ((OutputNode) rootNode).getChild();
assertEquals("table1", tableScanNode.getQualifiedTableName());
- assertEquals(8, tableScanNode.getOutputSymbols().size());
- assertEquals(8, tableScanNode.getAssignments().size());
+ assertEquals(
+ Arrays.asList("time", "tag1", "tag2", "tag3", "attr1", "attr2", "s1",
"s2", "s3"),
+ tableScanNode.getOutputColumnNames());
+ assertEquals(9, tableScanNode.getAssignments().size());
assertEquals(1, tableScanNode.getDeviceEntries().size());
assertEquals(5, tableScanNode.getIdAndAttributeIndexMap().size());
assertEquals(ASC, tableScanNode.getScanOrder());
-
distributionPlanner = new TableDistributionPlanner(actualAnalysis,
logicalQueryPlan, context);
distributedQueryPlan = distributionPlanner.plan();
assertEquals(4, distributedQueryPlan.getInstances().size());
+
+ // value filter which can be pushed down
+ sql = "SELECT tag1, attr1, s2 FROM table1 where s1 > 1";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ assertNotNull(actualAnalysis);
+ assertEquals(1, actualAnalysis.getTables().size());
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ assertTrue(rootNode instanceof OutputNode);
+ assertTrue(rootNode.getChildren().get(0) instanceof ProjectNode);
+ assertTrue(rootNode.getChildren().get(0).getChildren().get(0) instanceof
TableScanNode);
+ tableScanNode = (TableScanNode)
rootNode.getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "attr1", "s1", "s2"),
tableScanNode.getOutputColumnNames());
+
+ // value filter which can not be pushed down
+ sql = "SELECT tag1, attr1, s2 FROM table1 where diff(s1) > 1";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ assertEquals(1, actualAnalysis.getTables().size());
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ assertTrue(rootNode instanceof OutputNode);
+ assertTrue(rootNode.getChildren().get(0) instanceof ProjectNode);
+ assertTrue(rootNode.getChildren().get(0).getChildren().get(0) instanceof
FilterNode);
+ assertTrue(
+ rootNode.getChildren().get(0).getChildren().get(0).getChildren().get(0)
+ instanceof TableScanNode);
+ tableScanNode =
+ (TableScanNode)
rootNode.getChildren().get(0).getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "attr1", "s1", "s2"),
tableScanNode.getOutputColumnNames());
+
+ sql = "SELECT tag1, attr1, s2 FROM table1 where diff(s1) + 1 > 1";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ assertEquals(1, actualAnalysis.getTables().size());
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ assertTrue(rootNode instanceof OutputNode);
+ assertTrue(rootNode.getChildren().get(0) instanceof ProjectNode);
+ assertTrue(rootNode.getChildren().get(0).getChildren().get(0) instanceof
FilterNode);
+ assertTrue(
+ rootNode.getChildren().get(0).getChildren().get(0).getChildren().get(0)
+ instanceof TableScanNode);
+ tableScanNode =
+ (TableScanNode)
rootNode.getChildren().get(0).getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "attr1", "s1", "s2"),
tableScanNode.getOutputColumnNames());
+ }
+
+ @Test
+ public void singleTableProjectTest() throws IoTDBException {
+ // 1. project without filter
+ sql = "SELECT tag1, attr1, s1 FROM table1";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ assertNotNull(actualAnalysis);
+ assertEquals(1, actualAnalysis.getTables().size());
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ PlanNode tableScanNode =
rootNode.getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "attr1", "s1"),
tableScanNode.getOutputColumnNames());
+
+ // 2. project with filter
+ sql = "SELECT tag1, attr1, s1 FROM table1 WHERE tag2='A' and s2=8";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ assertNotNull(actualAnalysis);
+ assertEquals(1, actualAnalysis.getTables().size());
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ tableScanNode = rootNode.getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "tag2", "attr1", "s1", "s2"),
+ tableScanNode.getOutputColumnNames());
+
+ // 3. project with filter and function
+ sql =
+ "SELECT s1+s3, CAST(s2 AS DOUBLE) FROM table1 WHERE REPLACE(tag1,
'low', '!')='!' AND attr2='B'";
+ actualAnalysis = analyzeSQL(sql, metadata);
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ logicalQueryPlan =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP)
+ .plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ tableScanNode = rootNode.getChildren().get(0).getChildren().get(0);
+ assertEquals(
+ Arrays.asList("time", "tag1", "attr2", "s1", "s2", "s3"),
+ tableScanNode.getOutputColumnNames());
+ }
+
+ @Test
+ public void expressionTest() throws IoTDBException {
+ // 1. is null / is not null
+ sql = "SELECT * FROM table1 WHERE tag1 is not null and s1 is null";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalQueryPlan =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP)
+ .plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 2. like
+ sql = "SELECT * FROM table1 WHERE tag1 like '%m'";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalQueryPlan =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP)
+ .plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 3. in / not in
+ sql =
+ "SELECT *, s1/2, s2+1, s2*3, s1+s2, s2%1 FROM table1 WHERE tag1 in
('A', 'B') and tag2 not in ('A', 'C')";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalQueryPlan =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP)
+ .plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 4. not
+ sql = "SELECT * FROM table1 WHERE tag1 not like '%m'";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalQueryPlan =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP)
+ .plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+ }
+
+ @Test
+ public void functionTest() throws IoTDBException {
+ // 1. cast
+ sql = "SELECT CAST(s2 AS DOUBLE) FROM table1 WHERE CAST(s1 AS DOUBLE) >
1.0";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 2. substring
+ sql =
+ "SELECT SUBSTRING(tag1, 2), SUBSTRING(tag2, s1) FROM table1 WHERE
SUBSTRING(tag2, 1) = 'A'";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 3. round
+ sql = "SELECT ROUND(s1, 1) FROM table1 WHERE ROUND(s2, 2) > 1.0";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 4. replace
+ sql = "SELECT REPLACE(tag1, 'A', 'B') FROM table1 WHERE REPLACE(attr1,
'C', 'D') = 'D'";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
+
+ // 5. diff
+ sql = "SELECT DIFF(s1) FROM table1 WHERE DIFF(s2) > 0";
+ context = new MPPQueryContext(sql, queryId, sessionInfo, null, null);
+ actualAnalysis = analyzeSQL(sql, metadata);
+ logicalPlanner =
+ new LogicalPlanner(
+ context, metadata, sessionInfo, getFakePartitionFetcher(),
WarningCollector.NOOP);
+ logicalQueryPlan = logicalPlanner.plan(actualAnalysis);
+ rootNode = logicalQueryPlan.getRootNode();
}
public static Analysis analyzeSQL(String sql, Metadata metadata) {
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
index bbb6ef02d3c..02e01b29503 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
@@ -16,8 +16,6 @@ package
org.apache.iotdb.db.queryengine.plan.relational.analyzer;
import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
import org.apache.iotdb.commons.udf.builtin.BuiltinAggregationFunction;
-import org.apache.iotdb.commons.udf.builtin.BuiltinScalarFunction;
-import org.apache.iotdb.db.exception.sql.SemanticException;
import org.apache.iotdb.db.queryengine.common.SessionInfo;
import org.apache.iotdb.db.queryengine.plan.relational.function.OperatorType;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnMetadata;
@@ -33,7 +31,6 @@ import
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager;
import org.apache.iotdb.db.queryengine.plan.relational.type.TypeManager;
import
org.apache.iotdb.db.queryengine.plan.relational.type.TypeNotFoundException;
import org.apache.iotdb.db.queryengine.plan.relational.type.TypeSignature;
-import org.apache.iotdb.db.utils.constant.SqlConstant;
import org.apache.tsfile.file.metadata.StringArrayDeviceID;
import org.apache.tsfile.read.common.type.BinaryType;
@@ -44,6 +41,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Optional;
+import static
org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadataImpl.getFunctionType;
import static org.apache.tsfile.read.common.type.BinaryType.TEXT;
import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN;
import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE;
@@ -65,6 +63,7 @@ public class TestMatadata implements Metadata {
private static final String ATTR2 = "attr2";
private static final String S1 = "s1";
private static final String S2 = "s2";
+ private static final String S3 = "s3";
private static final ColumnMetadata TIME_CM = new ColumnMetadata(TIME,
INT64);
private static final ColumnMetadata TAG1_CM = new ColumnMetadata(TAG1,
BinaryType.TEXT);
private static final ColumnMetadata TAG2_CM = new ColumnMetadata(TAG2,
BinaryType.TEXT);
@@ -73,6 +72,7 @@ public class TestMatadata implements Metadata {
private static final ColumnMetadata ATTR2_CM = new ColumnMetadata(ATTR2,
BinaryType.TEXT);
private static final ColumnMetadata S1_CM = new ColumnMetadata(S1, INT64);
private static final ColumnMetadata S2_CM = new ColumnMetadata(S2, INT64);
+ private static final ColumnMetadata S3_CM = new ColumnMetadata(S3, DOUBLE);
public static final String DB2 = "db2";
public static final String TABLE2 = "table2";
@@ -101,6 +101,9 @@ public class TestMatadata implements Metadata {
.setColumnCategory(TsTableColumnCategory.MEASUREMENT)
.build(),
ColumnSchema.builder(S2_CM)
+ .setColumnCategory(TsTableColumnCategory.MEASUREMENT)
+ .build(),
+ ColumnSchema.builder(S3_CM)
.setColumnCategory(TsTableColumnCategory.MEASUREMENT)
.build());
@@ -150,114 +153,7 @@ public class TestMatadata implements Metadata {
@Override
public Type getFunctionReturnType(String functionName, List<? extends Type>
argumentTypes) {
-
- // builtin scalar function
- if
(BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName)
- ||
BuiltinScalarFunction.ROUND.getFunctionName().equalsIgnoreCase(functionName)) {
- if (!isOneNumericType(argumentTypes)) {
- throw new SemanticException(
- "Scalar function"
- + functionName.toLowerCase(Locale.ENGLISH)
- + " only supports numeric data types [INT32, INT64, FLOAT,
DOUBLE]");
- }
- return DOUBLE;
- } else if
(BuiltinScalarFunction.REPLACE.getFunctionName().equalsIgnoreCase(functionName)
- ||
BuiltinScalarFunction.SUBSTRING.getFunctionName().equalsIgnoreCase(functionName))
{
- if (!isOneTextType(argumentTypes)) {
- throw new SemanticException(
- "Scalar function"
- + functionName.toLowerCase(Locale.ENGLISH)
- + " only supports text data type.");
- }
- return TEXT;
- }
-
- // builtin aggregation function
- // check argument type
- switch (functionName.toLowerCase()) {
- case SqlConstant.AVG:
- case SqlConstant.SUM:
- case SqlConstant.EXTREME:
- case SqlConstant.MIN_VALUE:
- case SqlConstant.MAX_VALUE:
- case SqlConstant.STDDEV:
- case SqlConstant.STDDEV_POP:
- case SqlConstant.STDDEV_SAMP:
- case SqlConstant.VARIANCE:
- case SqlConstant.VAR_POP:
- case SqlConstant.VAR_SAMP:
- if (!isOneNumericType(argumentTypes)) {
- throw new SemanticException(
- String.format(
- "Aggregate functions [%s] only support numeric data types
[INT32, INT64, FLOAT, DOUBLE]",
- functionName));
- }
- break;
- case SqlConstant.MIN_TIME:
- case SqlConstant.MAX_TIME:
- case SqlConstant.FIRST_VALUE:
- case SqlConstant.LAST_VALUE:
- case SqlConstant.TIME_DURATION:
- case SqlConstant.MODE:
- if (argumentTypes.size() != 1) {
- throw new SemanticException(
- String.format(
- "Aggregate functions [%s] should only have one argument",
functionName));
- }
- break;
- case SqlConstant.MAX_BY:
- case SqlConstant.MIN_BY:
- if (argumentTypes.size() != 2) {
- throw new SemanticException(
- String.format(
- "Aggregate functions [%s] should only have two arguments",
functionName));
- } else if (!argumentTypes.get(1).isOrderable()) {
- throw new SemanticException(
- String.format(
- "Second argument of Aggregate functions [%s] should be
orderable", functionName));
- }
-
- break;
- case SqlConstant.COUNT:
- break;
- default:
- // ignore
- }
-
- // get return type
- switch (functionName.toLowerCase()) {
- case SqlConstant.MIN_TIME:
- case SqlConstant.MAX_TIME:
- case SqlConstant.COUNT:
- case SqlConstant.TIME_DURATION:
- return INT64;
- case SqlConstant.MIN_VALUE:
- case SqlConstant.LAST_VALUE:
- case SqlConstant.FIRST_VALUE:
- case SqlConstant.MAX_VALUE:
- case SqlConstant.EXTREME:
- case SqlConstant.MODE:
- case SqlConstant.MAX_BY:
- case SqlConstant.MIN_BY:
- return argumentTypes.get(0);
- case SqlConstant.AVG:
- case SqlConstant.SUM:
- case SqlConstant.STDDEV:
- case SqlConstant.STDDEV_POP:
- case SqlConstant.STDDEV_SAMP:
- case SqlConstant.VARIANCE:
- case SqlConstant.VAR_POP:
- case SqlConstant.VAR_SAMP:
- return DOUBLE;
- default:
- // ignore
- }
-
- // TODO scalar UDF function
-
- // TODO UDAF
-
- throw new SemanticException("Unknown function: " + functionName);
+ return getFunctionType(functionName, argumentTypes);
}
@Override