This is an automated email from the ASF dual-hosted git repository.

haonan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new dc3d9f1  [IOTDB-749] Handle select * from root OOM (#1884)
dc3d9f1 is described below

commit dc3d9f1555b94dd1a38514c3de0682241063fde3
Author: Steve Yurong Su <[email protected]>
AuthorDate: Thu Oct 29 11:42:59 2020 +0800

    [IOTDB-749] Handle select * from root OOM (#1884)
    
    * fix slimit logical errors in ConcatPathOptimizer
    
    * add ITs
    
    * fix tests
    
    * fix tests
    
    * set default MaxQueryDeduplicatedPathNum value back when 
IoTDBQueryMemoryControlIT is over
---
 .../qp/strategy/optimizer/ConcatPathOptimizer.java | 110 +++++------
 .../db/integration/IoTDBQueryMemoryControlIT.java  | 219 +++++++++++++++++++++
 2 files changed, 269 insertions(+), 60 deletions(-)

diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
 
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
index 5d089a6..eb41392 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
@@ -50,7 +50,6 @@ public class ConcatPathOptimizer implements ILogicalOptimizer 
{
   private static final String WARNING_NO_SUFFIX_PATHS = "given SFWOperator 
doesn't have suffix paths, cannot concat seriesPath";
   private static final String WARNING_NO_PREFIX_PATHS = "given SFWOperator 
doesn't have prefix paths, cannot concat seriesPath";
 
-
   @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity 
warning
   @Override
   public Operator transform(Operator operator, int maxDeduplicatedPathNum)
@@ -89,24 +88,12 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
 
     boolean isAlignByDevice = false;
     if (operator instanceof QueryOperator) {
-      if (!((QueryOperator) operator).isAlignByDevice() || ((QueryOperator) 
operator)
-          .isLastQuery()) {
-
-        // concat and remove star
-        if (((QueryOperator) operator).hasSlimit()) {
-          int seriesLimit = ((QueryOperator) operator).getSeriesLimit();
-          int seriesOffset = ((QueryOperator) operator).getSeriesOffset();
-          if (seriesLimit > maxDeduplicatedPathNum) {
-            throw new PathNumOverLimitException(maxDeduplicatedPathNum, 
seriesLimit);
-          }
-          concatSelect(prefixPaths, select, seriesLimit, seriesOffset);
-          slimitTrim(select, seriesOffset);
-        } else {
-          concatSelect(prefixPaths, select, maxDeduplicatedPathNum + 1, 0);
-          if (select.getSuffixPaths().size() > maxDeduplicatedPathNum) {
-            throw new PathNumOverLimitException(maxDeduplicatedPathNum);
-          }
-        }
+      if (!((QueryOperator) operator).isAlignByDevice()
+          || ((QueryOperator) operator).isLastQuery()) {
+        // concat paths and remove stars
+        int seriesLimit = ((QueryOperator) operator).getSeriesLimit();
+        int seriesOffset = ((QueryOperator) operator).getSeriesOffset();
+        concatSelect(prefixPaths, select, seriesLimit, seriesOffset, 
maxDeduplicatedPathNum);
       } else {
         isAlignByDevice = true;
         for (PartialPath path : initialSuffixPaths) {
@@ -171,8 +158,9 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
    * Extract paths from select&from cql, expand them into complete versions, 
and reassign them to
    * selectOperator's suffixPathList. Treat aggregations similarly.
    */
-  private void concatSelect(List<PartialPath> fromPaths, SelectOperator 
selectOperator, int limit, int offset)
-      throws LogicalOptimizeException {
+  private void concatSelect(List<PartialPath> fromPaths, SelectOperator 
selectOperator, int limit,
+      int offset, int maxDeduplicatedPathNum)
+      throws LogicalOptimizeException, PathNumOverLimitException {
     List<PartialPath> suffixPaths = judgeSelectOperator(selectOperator);
 
     List<PartialPath> allPaths = new ArrayList<>();
@@ -192,38 +180,12 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
       }
     }
 
-    removeStarsInPath(allPaths, afterConcatAggregations, selectOperator, 
limit, offset);
-  }
-
-  /**
-   * Make 'SOFFSET' take effect by trimming the suffixList and aggregations of 
the
-   * selectOperator.
-   *
-   * @param seriesOffset is ensured to be non-negative integer
-   */
-  private void slimitTrim(SelectOperator select, int seriesOffset)
-      throws LogicalOptimizeException {
-    List<PartialPath> suffixList = select.getSuffixPaths();
-    List<String> aggregations = select.getAggregations();
-    int size = suffixList.size();
-
-    // check parameter range
-    if (size == 0) {
-      throw new LogicalOptimizeException("SOFFSET <SOFFSETValue>: SOFFSETValue 
exceeds the range.");
-    }
-    int endPosition = seriesOffset + size;
-
-    // trim aggregations if exists
-    if (aggregations != null && !aggregations.isEmpty()) {
-      List<String> trimedAggregations = new ArrayList<>(
-          aggregations.subList(seriesOffset, endPosition));
-      select.setAggregations(trimedAggregations);
-    }
+    removeStarsInPath(allPaths, afterConcatAggregations, selectOperator, 
limit, offset,
+        maxDeduplicatedPathNum);
   }
 
   private FilterOperator concatFilter(List<PartialPath> fromPaths, 
FilterOperator operator,
-      Set<PartialPath> filterPaths)
-      throws LogicalOptimizeException {
+      Set<PartialPath> filterPaths) throws LogicalOptimizeException {
     if (!operator.isLeaf()) {
       List<FilterOperator> newFilterList = new ArrayList<>();
       for (FilterOperator child : operator.getChildren()) {
@@ -235,7 +197,8 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
     FunctionOperator functionOperator = (FunctionOperator) operator;
     PartialPath filterPath = functionOperator.getSinglePath();
     // do nothing in the cases of "where time > 5" or "where root.d1.s1 > 5"
-    if (SQLConstant.isReservedPath(filterPath) || 
filterPath.getFirstNode().startsWith(SQLConstant.ROOT)) {
+    if (SQLConstant.isReservedPath(filterPath) || filterPath.getFirstNode()
+        .startsWith(SQLConstant.ROOT)) {
       filterPaths.add(filterPath);
       return operator;
     }
@@ -259,8 +222,7 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
   }
 
   private FilterOperator constructBinaryFilterTreeWithAnd(List<PartialPath> 
noStarPaths,
-      FilterOperator operator)
-      throws LogicalOptimizeException {
+      FilterOperator operator) throws LogicalOptimizeException {
     FilterOperator filterBinaryTree = new FilterOperator(SQLConstant.KW_AND);
     FilterOperator currentNode = filterBinaryTree;
     for (int i = 0; i < noStarPaths.size(); i++) {
@@ -286,7 +248,8 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
    * @param paths list of paths which may contain stars
    * @return a unique seriesPath list
    */
-  private List<PartialPath> removeStarsInPathWithUnique(List<PartialPath> 
paths) throws LogicalOptimizeException {
+  private List<PartialPath> removeStarsInPathWithUnique(List<PartialPath> 
paths)
+      throws LogicalOptimizeException {
     List<PartialPath> retPaths = new ArrayList<>();
     HashSet<PartialPath> pathSet = new HashSet<>();
     try {
@@ -305,38 +268,65 @@ public class ConcatPathOptimizer implements 
ILogicalOptimizer {
     return retPaths;
   }
 
+  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity 
warning
   private void removeStarsInPath(List<PartialPath> paths, List<String> 
afterConcatAggregations,
-      SelectOperator selectOperator, int limit, int offset) throws 
LogicalOptimizeException {
+      SelectOperator selectOperator, int limit, int offset, int 
maxDeduplicatedPathNum)
+      throws LogicalOptimizeException, PathNumOverLimitException {
+    limit = limit == 0 || maxDeduplicatedPathNum < limit ? 
maxDeduplicatedPathNum + 1 : limit;
+    boolean hasOffset = offset != 0;
+    int consumed = 0;
     List<PartialPath> retPaths = new ArrayList<>();
     List<String> newAggregations = new ArrayList<>();
+
     for (int i = 0; i < paths.size(); i++) {
       try {
         Pair<List<PartialPath>, Integer> pair = removeWildcard(paths.get(i), 
limit, offset);
+
         List<PartialPath> actualPaths = pair.left;
-        offset = offset - pair.right;
         if (paths.get(i).getTsAlias() != null) {
           if (actualPaths.size() == 1) {
             actualPaths.get(0).setTsAlias(paths.get(i).getTsAlias());
           } else if (actualPaths.size() >= 2) {
             throw new LogicalOptimizeException(
-                "alias '" + paths.get(i).getTsAlias() + "' can only be matched 
with one time series");
+                "alias '" + paths.get(i).getTsAlias()
+                    + "' can only be matched with one time series");
           }
         }
         for (PartialPath actualPath : actualPaths) {
           retPaths.add(actualPath);
-          if (afterConcatAggregations != null && 
!afterConcatAggregations.isEmpty()) {
-            newAggregations.add(afterConcatAggregations.get(i));
+          extendListSafely(afterConcatAggregations, i, newAggregations);
+        }
+
+        consumed += pair.right;
+        if (offset != 0) {
+          int delta = offset - pair.right;
+          offset = Math.max(delta, 0);
+          if (delta < 0) {
+            limit += delta;
           }
+        } else {
+          limit -= pair.right;
+        }
+        if (limit == 0) {
+          if (retPaths.size() == maxDeduplicatedPathNum + 1) {
+            throw new PathNumOverLimitException(maxDeduplicatedPathNum);
+          }
+          break;
         }
       } catch (MetadataException e) {
         throw new LogicalOptimizeException("error when remove star: " + 
e.getMessage());
       }
     }
+
+    if (consumed == 0 ? hasOffset : retPaths.isEmpty()) {
+      throw new LogicalOptimizeException("SOFFSET <SOFFSETValue>: SOFFSETValue 
exceeds the range.");
+    }
     selectOperator.setSuffixPathList(retPaths);
     selectOperator.setAggregations(newAggregations);
   }
 
-  protected Pair<List<PartialPath>, Integer> removeWildcard(PartialPath path, 
int limit, int offset) throws MetadataException {
+  protected Pair<List<PartialPath>, Integer> removeWildcard(PartialPath path, 
int limit, int offset)
+      throws MetadataException {
     return IoTDB.metaManager.getAllTimeseriesPathWithAlias(path, limit, 
offset);
   }
 }
diff --git 
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryMemoryControlIT.java
 
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryMemoryControlIT.java
new file mode 100644
index 0000000..8a0f3ba
--- /dev/null
+++ 
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryMemoryControlIT.java
@@ -0,0 +1,219 @@
+/*
+ * 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.integration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IoTDBQueryMemoryControlIT {
+
+  private int defaultMaxQueryDeduplicatedPathNum;
+
+  private static final String[] sqls = new String[]{
+      "set storage group to root.ln",
+
+      "create timeseries root.ln.wf01.wt01 with 
datatype=BOOLEAN,encoding=PLAIN",
+      "create timeseries root.ln.wf01.wt02 with 
datatype=BOOLEAN,encoding=PLAIN",
+      "create timeseries root.ln.wf01.wt03 with 
datatype=BOOLEAN,encoding=PLAIN",
+      "create timeseries root.ln.wf01.wt04 with 
datatype=BOOLEAN,encoding=PLAIN",
+      "create timeseries root.ln.wf01.wt05 with 
datatype=BOOLEAN,encoding=PLAIN",
+
+      "create timeseries root.ln.wf02.wt01 with datatype=FLOAT,encoding=RLE",
+      "create timeseries root.ln.wf02.wt02 with datatype=FLOAT,encoding=RLE",
+      "create timeseries root.ln.wf02.wt03 with datatype=FLOAT,encoding=RLE",
+      "create timeseries root.ln.wf02.wt04 with datatype=FLOAT,encoding=RLE",
+      "create timeseries root.ln.wf02.wt05 with datatype=FLOAT,encoding=RLE",
+
+      "create timeseries root.ln.wf03.wt01 with datatype=TEXT,encoding=PLAIN",
+      "create timeseries root.ln.wf03.wt02 with datatype=TEXT,encoding=PLAIN",
+      "create timeseries root.ln.wf03.wt03 with datatype=TEXT,encoding=PLAIN",
+      "create timeseries root.ln.wf03.wt04 with datatype=TEXT,encoding=PLAIN",
+      "create timeseries root.ln.wf03.wt05 with datatype=TEXT,encoding=PLAIN",
+  };
+
+  @Before
+  public void setUp() throws Exception {
+    defaultMaxQueryDeduplicatedPathNum = 
IoTDBDescriptor.getInstance().getConfig()
+        .getMaxQueryDeduplicatedPathNum();
+    
IoTDBDescriptor.getInstance().getConfig().setMaxQueryDeduplicatedPathNum(10);
+    EnvironmentUtils.envSetUp();
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    createTimeSeries();
+  }
+
+  private static void createTimeSeries() {
+    try (Statement statement = DriverManager.getConnection(
+        Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root").createStatement()) {
+      for (String sql : sqls) {
+        statement.execute(sql);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    EnvironmentUtils.cleanEnv();
+    IoTDBDescriptor.getInstance().getConfig()
+        .setMaxQueryDeduplicatedPathNum(defaultMaxQueryDeduplicatedPathNum);
+  }
+
+  @Test
+  public void selectWildcard() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select * from root");
+    } catch (Exception e) {
+      assertTrue(e.getMessage().contains("Too many paths in one query!"));
+    }
+  }
+
+  @Test
+  public void selectWildcardSlimit10() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select * from root slimit 10");
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void selectWildcardSlimit11() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select * from root slimit 11");
+    } catch (Exception e) {
+      assertTrue(e.getMessage().contains("Too many paths in one query!"));
+    }
+  }
+
+  @Test
+  public void selectWildcardWildcardWildcardSlimit5Soffset7() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select wf01.*, wf02.*, wf03.* from root.ln slimit 5 
soffset 7");
+      ResultSetMetaData resultSetMetaData = 
statement.getResultSet().getMetaData();
+      assertEquals(1 + 5, resultSetMetaData.getColumnCount());
+      for (int i = 2; i < 3 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf02.wt0"));
+      }
+      for (int i = 3 + 2; i < 5 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf03.wt0"));
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void selectWildcardWildcardWildcardSlimit5Soffset5() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select wf01.*, wf02.*, wf03.* from root.ln slimit 5 
soffset 5");
+      ResultSetMetaData resultSetMetaData = 
statement.getResultSet().getMetaData();
+      assertEquals(1 + 5, resultSetMetaData.getColumnCount());
+      for (int i = 2; i < 5 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf02.wt0"));
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void selectWildcardWildcardWildcardSlimit15Soffset5() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select wf01.*, wf03.*, wf02.* from root.ln slimit 15 
soffset 5");
+      ResultSetMetaData resultSetMetaData = 
statement.getResultSet().getMetaData();
+      assertEquals(1 + 10, resultSetMetaData.getColumnCount());
+      for (int i = 2; i < 5 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf03.wt0"));
+      }
+      for (int i = 5 + 2; i < 10 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf02.wt0"));
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void selectWildcardWildcardWildcardSlimit15Soffset4() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select wf01.*, wf02.*, wf03.* from root.ln slimit 15 
soffset 4");
+    } catch (Exception e) {
+      assertTrue(e.getMessage().contains("Too many paths in one query!"));
+    }
+  }
+
+  @Test
+  public void selectWildcardWildcardWildcardSlimit3Soffset4() {
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", 
"root");
+        Statement statement = connection.createStatement()) {
+      statement.execute("select wf01.*, wf02.*, wf03.* from root.ln slimit 3 
soffset 4");
+      ResultSetMetaData resultSetMetaData = 
statement.getResultSet().getMetaData();
+      assertEquals(1 + 3, resultSetMetaData.getColumnCount());
+      for (int i = 2; i < 1 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf01.wt0"));
+      }
+      for (int i = 1 + 2; i < 3 + 2; ++i) {
+        System.out.println(resultSetMetaData.getColumnName(i));
+        
assertTrue(resultSetMetaData.getColumnName(i).contains("root.ln.wf02.wt0"));
+      }
+    } catch (Exception e) {
+      assertTrue(e.getMessage().contains("Too many paths in one query!"));
+    }
+  }
+}

Reply via email to