You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by hu...@apache.org on 2022/10/11 09:48:23 UTC

[iotdb] 02/04: tmp save (analyzer for SELECT INTO)

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 <li...@foxmail.com>
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");
+      }
     }
   }