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

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

commit af424d5e16f570fea7175885f94b26c63a3769cb
Author: Beyyes <[email protected]>
AuthorDate: Mon Oct 28 19:34:38 2024 +0800

    fix having
---
 .../aggregation/LastValueAccumulator.java          | 16 ++++-
 .../execution/operator/AggregationUtil.java        |  2 +
 .../queryengine/plan/analyze/AnalyzeVisitor.java   | 25 +++++--
 .../plan/analyze/ExpressionAnalyzer.java           | 13 ++++
 .../queryengine/plan/analyze/ExpressionUtils.java  | 33 ++++++---
 .../visitor/ColumnTransformerVisitor.java          | 12 +++-
 .../visitor/ExistUnknownTypeInExpression.java      | 64 ++++++++++++++++++
 ...catDeviceAndBindSchemaForExpressionVisitor.java |  2 +
 .../ConcatDeviceAndBindSchemaForHavingVisitor.java | 79 ++++++++++++++++++++++
 9 files changed, 228 insertions(+), 18 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java
index 603f3427860..f1b78209954 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java
@@ -39,7 +39,9 @@ public class LastValueAccumulator implements Accumulator {
 
   public LastValueAccumulator(TSDataType seriesDataType) {
     this.seriesDataType = seriesDataType;
-    lastValue = TsPrimitiveType.getByType(seriesDataType);
+    if (seriesDataType != TSDataType.UNKNOWN) {
+      lastValue = TsPrimitiveType.getByType(seriesDataType);
+    }
   }
 
   // Column should be like: | Time | Value |
@@ -68,6 +70,8 @@ public class LastValueAccumulator implements Accumulator {
       case BOOLEAN:
         addBooleanInput(columns, bitMap);
         return;
+      case UNKNOWN:
+        return;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Unsupported data type in LastValue: %s", 
seriesDataType));
@@ -104,6 +108,8 @@ public class LastValueAccumulator implements Accumulator {
       case BOOLEAN:
         updateBooleanLastValue(partialResult[0].getBoolean(0), 
partialResult[1].getLong(0));
         break;
+      case UNKNOWN:
+        break;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Unsupported data type in LastValue: %s", 
seriesDataType));
@@ -138,6 +144,8 @@ public class LastValueAccumulator implements Accumulator {
       case BOOLEAN:
         updateBooleanLastValue((boolean) statistics.getLastValue(), 
statistics.getEndTime());
         break;
+      case UNKNOWN:
+        break;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Unsupported data type in LastValue: %s", 
seriesDataType));
@@ -173,6 +181,8 @@ public class LastValueAccumulator implements Accumulator {
         case BOOLEAN:
           lastValue.setBoolean(finalResult.getBoolean(0));
           break;
+        case UNKNOWN:
+          break;
         default:
           throw new UnSupportedDataTypeException(
               String.format("Unsupported data type in LastValue: %s", 
seriesDataType));
@@ -212,6 +222,8 @@ public class LastValueAccumulator implements Accumulator {
       case BOOLEAN:
         columnBuilders[0].writeBoolean(lastValue.getBoolean());
         break;
+      case UNKNOWN:
+        break;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Unsupported data type in Extreme: %s", 
seriesDataType));
@@ -248,6 +260,8 @@ public class LastValueAccumulator implements Accumulator {
       case BOOLEAN:
         columnBuilder.writeBoolean(lastValue.getBoolean());
         break;
+      case UNKNOWN:
+        break;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Unsupported data type in Extreme: %s", 
seriesDataType));
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationUtil.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationUtil.java
index 6bcfa9a3169..b76435a2542 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationUtil.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationUtil.java
@@ -261,6 +261,8 @@ public class AggregationUtil {
       case BLOB:
       case STRING:
         return StatisticsManager.getInstance().getMaxBinarySizeInBytes();
+      case UNKNOWN:
+        return 0;
       default:
         throw new UnsupportedOperationException("Unknown data type " + 
tsDataType);
     }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
index a98afe09f58..7b2a8230bd5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
@@ -85,6 +85,7 @@ import 
org.apache.iotdb.db.queryengine.plan.expression.binary.CompareBinaryExpre
 import org.apache.iotdb.db.queryengine.plan.expression.leaf.ConstantOperand;
 import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
 import 
org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.ExistUnknownTypeInExpression;
 import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.MeasurementGroup;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.DeviceViewIntoPathDescriptor;
@@ -210,6 +211,8 @@ import static 
org.apache.iotdb.db.queryengine.plan.analyze.AnalyzeUtils.removeLo
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.AnalyzeUtils.validateSchema;
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.bindSchemaForExpression;
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression;
+import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.concatDeviceAndBindSchemaForHaving;
+import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.concatDeviceAndBindSchemaForPredicate;
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.getMeasurementExpression;
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.normalizeExpression;
 import static 
org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.searchAggregationExpressions;
@@ -1074,8 +1077,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
 
     for (PartialPath device : deviceSet) {
       List<Expression> expressionsInHaving =
-          concatDeviceAndBindSchemaForExpression(
-              havingExpression, device, schemaTree, queryContext);
+          concatDeviceAndBindSchemaForHaving(havingExpression, device, 
schemaTree, queryContext);
 
       conJunctions.addAll(
           expressionsInHaving.stream()
@@ -1086,11 +1088,15 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         Set<Expression> aggregationExpressions = new LinkedHashSet<>();
         Set<Expression> normalizedAggregationExpressions = new 
LinkedHashSet<>();
         for (Expression aggregationExpression : 
searchAggregationExpressions(expression)) {
-          Expression normalizedAggregationExpression = 
normalizeExpression(aggregationExpression);
 
+          Expression normalizedAggregationExpression = 
normalizeExpression(aggregationExpression);
           analyzeExpressionType(analysis, aggregationExpression);
           analyzeExpressionType(analysis, normalizedAggregationExpression);
 
+          if (!new 
ExistUnknownTypeInExpression().process(aggregationExpression, null).isEmpty()) {
+            continue;
+          }
+
           aggregationExpressions.add(aggregationExpression);
           
normalizedAggregationExpressions.add(normalizedAggregationExpression);
         }
@@ -1103,7 +1109,11 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       }
     }
 
-    havingExpression = PredicateUtils.combineConjuncts(new 
ArrayList<>(conJunctions));
+    if (conJunctions.isEmpty()) {
+      havingExpression = queryStatement.getHavingCondition().getPredicate();
+    } else {
+      havingExpression = PredicateUtils.combineConjuncts(new 
ArrayList<>(conJunctions));
+    }
     TSDataType outputType = analyzeExpressionType(analysis, havingExpression);
     if (outputType != TSDataType.BOOLEAN) {
       throw new SemanticException(
@@ -1552,7 +1562,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       final ISchemaTree schemaTree,
       final MPPQueryContext queryContext) {
     List<Expression> conJunctions =
-        ExpressionAnalyzer.concatDeviceAndBindSchemaForPredicate(
+        concatDeviceAndBindSchemaForPredicate(
             queryStatement.getWhereCondition().getPredicate(),
             devicePath,
             schemaTree,
@@ -2480,7 +2490,10 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       Analysis analysis, List<Expression> expressions) {
     TSDataType checkedDataType = analysis.getType(expressions.get(0));
     for (Expression expression : expressions) {
-      if (analysis.getType(expression) != checkedDataType) {
+      if (analysis.getType(expression) == TSDataType.UNKNOWN) {
+        analysis.setExpressionType(expression, checkedDataType);
+      } else if (checkedDataType != TSDataType.UNKNOWN
+          && analysis.getType(expression) != checkedDataType) {
         throw new SemanticException(
             "ALIGN BY DEVICE: the data types of the same measurement column 
should be the same across devices.");
       }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionAnalyzer.java
index af9f1a5d094..3df288cbdaa 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionAnalyzer.java
@@ -52,6 +52,7 @@ import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.ReplaceSubTreeWit
 import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.BindSchemaForExpressionVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.BindSchemaForPredicateVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.ConcatDeviceAndBindSchemaForExpressionVisitor;
+import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.ConcatDeviceAndBindSchemaForHavingVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.ConcatDeviceAndBindSchemaForPredicateVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.ConcatExpressionWithSuffixPathsVisitor;
 import org.apache.iotdb.db.queryengine.plan.statement.component.ResultColumn;
@@ -490,6 +491,18 @@ public class ExpressionAnalyzer {
                 devicePath, schemaTree, isWhere, queryContext));
   }
 
+  public static List<Expression> concatDeviceAndBindSchemaForHaving(
+      final Expression predicate,
+      final PartialPath devicePath,
+      final ISchemaTree schemaTree,
+      final MPPQueryContext queryContext) {
+    return new ConcatDeviceAndBindSchemaForHavingVisitor()
+        .process(
+            predicate,
+            new ConcatDeviceAndBindSchemaForHavingVisitor.Context(
+                devicePath, schemaTree, queryContext));
+  }
+
   /**
    * Search for subexpressions that can be queried natively, including all 
time series.
    *
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
index e8eb2d69760..a0836f9dcbe 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
@@ -33,6 +33,7 @@ import 
org.apache.iotdb.db.queryengine.plan.expression.binary.GreaterThanExpress
 import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LessEqualExpression;
 import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LessThanExpression;
 import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LogicAndExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LogicBinaryExpression;
 import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LogicOrExpression;
 import org.apache.iotdb.db.queryengine.plan.expression.binary.ModuloExpression;
 import 
org.apache.iotdb.db.queryengine.plan.expression.binary.MultiplicationExpression;
@@ -200,7 +201,28 @@ public class ExpressionUtils {
       final List<Expression> rightExpressions,
       final MPPQueryContext queryContext) {
     List<Expression> resultExpressions = new ArrayList<>();
-    if (!leftExpressions.isEmpty() && !rightExpressions.isEmpty()) {
+    if (expression instanceof LogicBinaryExpression) {
+      if (!leftExpressions.isEmpty() && !rightExpressions.isEmpty()) {
+        for (Expression le : leftExpressions) {
+          for (Expression re : rightExpressions) {
+            resultExpressions.add(
+                reserveMemoryForExpression(
+                    queryContext, reconstructBinaryExpression(expression, le, 
re)));
+          }
+        }
+        return resultExpressions;
+      } else if (!leftExpressions.isEmpty()) {
+        for (Expression le : leftExpressions) {
+          resultExpressions.add(reserveMemoryForExpression(queryContext, le));
+        }
+      } else if (!rightExpressions.isEmpty()) {
+        for (Expression re : rightExpressions) {
+          resultExpressions.add(reserveMemoryForExpression(queryContext, re));
+        }
+      }
+      return resultExpressions;
+    } else {
+
       for (Expression le : leftExpressions) {
         for (Expression re : rightExpressions) {
           resultExpressions.add(
@@ -209,16 +231,7 @@ public class ExpressionUtils {
         }
       }
       return resultExpressions;
-    } else if (!leftExpressions.isEmpty()) {
-      for (Expression le : leftExpressions) {
-        resultExpressions.add(reserveMemoryForExpression(queryContext, le));
-      }
-    } else if (!rightExpressions.isEmpty()) {
-      for (Expression re : rightExpressions) {
-        resultExpressions.add(reserveMemoryForExpression(queryContext, re));
-      }
     }
-    return resultExpressions;
   }
 
   public static Expression reconstructBinaryExpression(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
index 498cac300fb..0e930f1fe77 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
@@ -83,6 +83,7 @@ import java.util.Map;
 import java.util.stream.Collectors;
 
 import static 
org.apache.iotdb.db.queryengine.plan.expression.ExpressionType.BETWEEN;
+import static org.apache.tsfile.enums.TSDataType.UNKNOWN;
 
 /** Responsible for constructing {@link ColumnTransformer} through Expression. 
*/
 public class ColumnTransformerVisitor
@@ -584,7 +585,16 @@ public class ColumnTransformerVisitor
       if (typeProvider != null) {
         return typeProvider.getTreeModelType(expression.getOutputSymbol());
       }
-      return expressionTypes.get(NodeRef.of(expression));
+      if (expressionTypes.get(NodeRef.of(expression)) != UNKNOWN) {
+        return expressionTypes.get(NodeRef.of(expression));
+      } else {
+        for (Map.Entry<NodeRef<Expression>, TSDataType> entry : 
expressionTypes.entrySet()) {
+          if (entry.getKey().getNode().equals(expression) && entry.getValue() 
!= UNKNOWN) {
+            return entry.getValue();
+          }
+        }
+        return UNKNOWN;
+      }
     }
 
     public TypeProvider getTypeProvider() {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ExistUnknownTypeInExpression.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ExistUnknownTypeInExpression.java
new file mode 100644
index 00000000000..ce453596659
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ExistUnknownTypeInExpression.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.plan.expression.visitor;
+
+import org.apache.iotdb.db.queryengine.plan.expression.Expression;
+import org.apache.iotdb.db.queryengine.plan.expression.leaf.LeafOperand;
+import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
+import 
org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
+
+import org.apache.tsfile.enums.TSDataType;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ExistUnknownTypeInExpression extends CollectVisitor {
+
+  @Override
+  public List<Expression> visitLeafOperand(LeafOperand leafOperand, Void 
context) {
+    //        if (leafOperand instanceof NullOperand) {
+    //            return Collections.singletonList(leafOperand);
+    //        }
+
+    return Collections.emptyList();
+  }
+
+  @Override
+  public List<Expression> visitFunctionExpression(
+      FunctionExpression functionExpression, Void context) {
+    List<List<Expression>> ret = getResultsFromChild(functionExpression, null);
+    for (List<Expression> row : ret) {
+      if (!row.isEmpty()) {
+        return row;
+      }
+    }
+    return Collections.emptyList();
+  }
+
+  @Override
+  public List<Expression> visitTimeSeriesOperand(
+      TimeSeriesOperand timeSeriesOperand, Void context) {
+    if (timeSeriesOperand.getPath().getSeriesType() == TSDataType.UNKNOWN) {
+      return Collections.singletonList(timeSeriesOperand);
+    }
+
+    return Collections.emptyList();
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForExpressionVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForExpressionVisitor.java
index fc0bdb6f669..79c3b9d4936 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForExpressionVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForExpressionVisitor.java
@@ -99,6 +99,8 @@ public class ConcatDeviceAndBindSchemaForExpressionVisitor
         context.getSchemaTree().searchMeasurementPaths(concatPath).left;
     if (actualPaths.isEmpty()) {
       return Collections.emptyList();
+      // return Collections.singletonList(new TimeSeriesOperand(new 
MeasurementPath(concatPath,
+      // TSDataType.UNKNOWN)));
     }
 
     // process logical view
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForHavingVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForHavingVisitor.java
new file mode 100644
index 00000000000..0aba03a8023
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForHavingVisitor.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian;
+
+import org.apache.iotdb.commons.path.MeasurementPath;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.exception.sql.SemanticException;
+import org.apache.iotdb.db.queryengine.plan.analyze.ExpressionUtils;
+import org.apache.iotdb.db.queryengine.plan.expression.Expression;
+import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
+
+import org.apache.tsfile.enums.TSDataType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static 
org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.BindSchemaForExpressionVisitor.transformViewPath;
+
+public class ConcatDeviceAndBindSchemaForHavingVisitor
+    extends ConcatDeviceAndBindSchemaForExpressionVisitor {
+  @Override
+  public List<Expression> visitTimeSeriesOperand(
+      TimeSeriesOperand timeSeriesOperand, Context context) {
+    PartialPath measurement = timeSeriesOperand.getPath();
+    PartialPath concatPath = context.getDevicePath().concatPath(measurement);
+
+    List<MeasurementPath> actualPaths =
+        context.getSchemaTree().searchMeasurementPaths(concatPath).left;
+    if (actualPaths.isEmpty()) {
+      // return Collections.emptyList();
+      return Collections.singletonList(
+          new TimeSeriesOperand(new MeasurementPath(concatPath, 
TSDataType.UNKNOWN)));
+    }
+
+    // process logical view
+    List<MeasurementPath> nonViewActualPaths = new ArrayList<>();
+    List<MeasurementPath> viewPaths = new ArrayList<>();
+    for (MeasurementPath measurementPath : actualPaths) {
+      if (measurementPath.getMeasurementSchema().isLogicalView()) {
+        viewPaths.add(measurementPath);
+      } else {
+        nonViewActualPaths.add(measurementPath);
+      }
+    }
+    List<Expression> reconstructTimeSeriesOperands =
+        ExpressionUtils.reconstructTimeSeriesOperandsWithMemoryCheck(
+            timeSeriesOperand, nonViewActualPaths, context.getQueryContext());
+    // handle logical views
+    for (MeasurementPath measurementPath : viewPaths) {
+      Expression replacedExpression = transformViewPath(measurementPath, 
context.getSchemaTree());
+      if (!(replacedExpression instanceof TimeSeriesOperand)) {
+        throw new SemanticException(
+            "Only writable view timeseries are supported in ALIGN BY DEVICE 
queries.");
+      }
+
+      replacedExpression.setViewPath(measurementPath);
+      reconstructTimeSeriesOperands.add(replacedExpression);
+    }
+    return reconstructTimeSeriesOperands;
+  }
+}

Reply via email to