This is an automated email from the ASF dual-hosted git repository. hui pushed a commit to branch lmh/mppSelectInto in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 62b2d495b9f2b1e7a61a6f54400edb4ee17f1ab1 Author: Minghui Liu <[email protected]> AuthorDate: Mon Sep 19 10:22:55 2022 +0800 tmp save (analyzer for SELECT INTO) --- .../apache/iotdb/commons/conf/IoTDBConstant.java | 5 ++ .../org/apache/iotdb/commons/path/PartialPath.java | 13 ++++ .../iotdb/db/metadata/path/MeasurementPath.java | 4 ++ .../apache/iotdb/db/mpp/plan/analyze/Analysis.java | 22 +++++++ .../iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java | 72 ++++++++++++++++++++++ .../db/mpp/plan/statement/crud/QueryStatement.java | 14 +++++ 6 files changed, 130 insertions(+) diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java index 5d2c219e60..536fb56513 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java @@ -20,6 +20,7 @@ package org.apache.iotdb.commons.conf; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; public class IoTDBConstant { @@ -260,4 +261,8 @@ public class IoTDBConstant { V_0_12, V_0_13 } + + // select into + public static final Pattern LEVELED_PATH_TEMPLATE_PATTERN = Pattern.compile("\\$\\{\\w+}"); + public static final String DOUBLE_COLONS = "::"; } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java index bec99ab45f..f745444798 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java @@ -550,6 +550,19 @@ public class PartialPath extends Path implements Comparable<Path>, Cloneable { return true; } + public boolean startWith(String otherNode) { + return nodes[0].equals(otherNode); + } + + public boolean containNode(String otherNode) { + for (String node : nodes) { + if (node.equals(otherNode)) { + return true; + } + } + return false; + } + @Override public String toString() { return getFullPath(); diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/path/MeasurementPath.java b/server/src/main/java/org/apache/iotdb/db/metadata/path/MeasurementPath.java index 2525844e31..0de9c3bd43 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/path/MeasurementPath.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/path/MeasurementPath.java @@ -57,6 +57,10 @@ public class MeasurementPath extends PartialPath { this.measurementSchema = new MeasurementSchema(getMeasurement(), type); } + public MeasurementPath(PartialPath path, TSDataType type, boolean isUnderAlignedEntity) { + this(path, new MeasurementSchema(path.getMeasurement(), type), isUnderAlignedEntity); + } + public MeasurementPath(PartialPath measurementPath, IMeasurementSchema measurementSchema) { this(measurementPath, measurementSchema, false); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java index d45d98e22c..28639dfd4a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java @@ -96,6 +96,9 @@ public class Analysis { private boolean isRawDataSource; + // map from output column to target into path + private Map<String, PartialPath> outputColumnToIntoPathMap; + ///////////////////////////////////////////////////////////////////////////////////////////////// // Query Analysis (used in ALIGN BY DEVICE) ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -124,6 +127,9 @@ public class Analysis { private Map<String, Boolean> deviceToIsRawDataSource; + // map from device name to target into path of each output column + private Map<String, Map<String, PartialPath>> deviceToIntoPathMap; + ///////////////////////////////////////////////////////////////////////////////////////////////// // Query Common Analysis (above DeviceView) ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -465,4 +471,20 @@ public class Analysis { public void setOutputExpressions(List<Pair<Expression, String>> outputExpressions) { this.outputExpressions = outputExpressions; } + + public Map<String, PartialPath> getOutputColumnToIntoPathMap() { + return outputColumnToIntoPathMap; + } + + public void setOutputColumnToIntoPathMap(Map<String, PartialPath> outputColumnToIntoPathMap) { + this.outputColumnToIntoPathMap = outputColumnToIntoPathMap; + } + + public Map<String, Map<String, PartialPath>> getDeviceToIntoPathMap() { + return deviceToIntoPathMap; + } + + public void setDeviceToIntoPathMap(Map<String, Map<String, PartialPath>> deviceToIntoPathMap) { + this.deviceToIntoPathMap = deviceToIntoPathMap; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java index a5dace95a8..baa7a465e3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java @@ -49,6 +49,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; +import org.apache.iotdb.db.mpp.plan.statement.component.AlignByTimeIntoComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent; import org.apache.iotdb.db.mpp.plan.statement.component.Ordering; @@ -92,6 +93,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTempla import org.apache.iotdb.db.mpp.plan.statement.sys.ExplainStatement; import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement; import org.apache.iotdb.db.mpp.plan.statement.sys.sync.ShowPipeSinkTypeStatement; +import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.GroupByFilter; import org.apache.iotdb.tsfile.read.filter.GroupByMonthFilter; @@ -117,6 +119,8 @@ import java.util.TimeZone; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.commons.conf.IoTDBConstant.DOUBLE_COLONS; +import static org.apache.iotdb.commons.conf.IoTDBConstant.LEVELED_PATH_TEMPLATE_PATTERN; import static org.apache.iotdb.commons.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD; import static org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD; import static org.apache.iotdb.db.metadata.MetadataConstant.ALL_RESULT_NODES; @@ -238,6 +242,8 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> deviceToTransformExpressions, deviceToMeasurementsMap); + analyzeInto(analysis, outputExpressions, deviceToMeasurementsMap); + if (queryStatement.hasHaving()) { List<PartialPath> measurementNotExistDevices = new ArrayList<>(); for (PartialPath device : deviceList) { @@ -417,6 +423,8 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analysis.setRawPathToGroupedPathMap(rawPathToGroupedPathMap); } + analyzeInto(analysis, outputExpressions); + // true if nested expressions and UDFs exist in aggregation function // i.e. select sum(s1 + 1) from root.sg.d1 boolean isHasRawDataInputAggregation = false; @@ -989,6 +997,70 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> ExpressionTypeAnalyzer.analyzeExpression(analysis, expression); } + private void analyzeInto(Analysis analysis, List<Pair<Expression, String>> outputExpressions) { + Map<String, PartialPath> outputColumnToIntoPathMap = new HashMap<>(); + List<Expression> outputColumns = + outputExpressions.stream() + .map(Pair::getLeft) + .collect(Collectors.toCollection(ArrayList::new)); + AlignByTimeIntoComponent intoComponent = + ((AlignByTimeIntoComponent) ((QueryStatement) analysis.getStatement()).getIntoComponent()); + List<PartialPath> intoPaths = intoComponent.getIntoPaths(); + boolean isAligned = intoComponent.isAligned(); + + boolean isAllRawSeriesQuery = checkIsAllRawSeriesQuery(outputColumns); + if (isAllRawSeriesQuery) { + if (intoPaths.size() != outputColumns.size()) { + throw new SemanticException( + "select into: the number of source paths and the number of target paths should be the same."); + } + if (intoPaths.size() > new HashSet<>(intoPaths).size()) { + throw new SemanticException( + "select into: target paths in into clause should be different."); + } + for (int i = 0; i < outputColumns.size(); i++) { + outputColumnToIntoPathMap.put( + outputColumns.get(i).toString(), + constructIntoPath(analysis, outputColumns.get(i), intoPaths.get(i), isAligned)); + } + } else { + + } + analysis.setOutputColumnToIntoPathMap(outputColumnToIntoPathMap); + } + + private PartialPath constructIntoPath( + Analysis analysis, Expression outputColumn, PartialPath path, boolean isAligned) { + if (!path.startWith(SQLConstant.ROOT)) { + throw new SemanticException("select into: "); + } + if (path.containNode(DOUBLE_COLONS)) { + throw new SemanticException("select into: "); + } + if (LEVELED_PATH_TEMPLATE_PATTERN.matcher(path.getFullPath()).find()) { + throw new SemanticException("select into: "); + } + return new MeasurementPath(path, analysis.getType(outputColumn), isAligned); + } + + private void analyzeInto( + Analysis analysis, + List<Pair<Expression, String>> outputExpressions, + Map<String, Set<String>> deviceToMeasurementsMap) { + Map<String, Map<String, PartialPath>> deviceToIntoPathMap = new HashMap<>(); + + analysis.setDeviceToIntoPathMap(deviceToIntoPathMap); + } + + private boolean checkIsAllRawSeriesQuery(List<Expression> expressions) { + for (Expression expression : expressions) { + if (!(expression instanceof TimeSeriesOperand)) { + return true; + } + } + return false; + } + /** * Check datatype consistency in ALIGN BY DEVICE. * diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java index 1d4490ce3c..f6f9578f59 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java @@ -27,6 +27,8 @@ import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; +import org.apache.iotdb.db.mpp.plan.statement.component.AlignByDeviceIntoComponent; +import org.apache.iotdb.db.mpp.plan.statement.component.AlignByTimeIntoComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByLevelComponent; @@ -238,6 +240,10 @@ public class QueryStatement extends Statement { return groupByTimeComponent != null; } + public boolean isAlignByTime() { + return resultSetFormat == ResultSetFormat.ALIGN_BY_TIME; + } + public boolean isAlignByDevice() { return resultSetFormat == ResultSetFormat.ALIGN_BY_DEVICE; } @@ -389,6 +395,14 @@ public class QueryStatement extends Statement { if (isLastQuery()) { throw new SemanticException("select into: last clauses are not supported."); } + if (isAlignByDevice() && intoComponent instanceof AlignByTimeIntoComponent) { + throw new SemanticException( + "select into: target path is illegal, expected: full path or suffix path"); + } + if (isAlignByTime() && intoComponent instanceof AlignByDeviceIntoComponent) { + throw new SemanticException( + "select into: target path is illegal, expected: target device and measurements"); + } } }
