This is an automated email from the ASF dual-hosted git repository.
jackietien 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 a4065454b45 Fix the case that there is nonexist measurement in having
clause
a4065454b45 is described below
commit a4065454b45fd35d55de20748b5cba2522ce3062
Author: Beyyes <[email protected]>
AuthorDate: Tue Oct 29 10:04:22 2024 +0800
Fix the case that there is nonexist measurement in having clause
---
.../db/it/alignbydevice/IoTDBAlignByDeviceIT.java | 45 +++++++++++++
.../queryengine/plan/analyze/AnalyzeVisitor.java | 9 ++-
.../plan/analyze/ExpressionAnalyzer.java | 13 ++++
.../visitor/ColumnTransformerVisitor.java | 15 ++++-
.../visitor/ExistUnknownTypeInExpression.java | 60 +++++++++++++++++
.../ConcatDeviceAndBindSchemaForHavingVisitor.java | 77 ++++++++++++++++++++++
6 files changed, 216 insertions(+), 3 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/db/it/alignbydevice/IoTDBAlignByDeviceIT.java
b/integration-test/src/test/java/org/apache/iotdb/db/it/alignbydevice/IoTDBAlignByDeviceIT.java
index cc5fdeb5b27..1bb3e76061d 100644
---
a/integration-test/src/test/java/org/apache/iotdb/db/it/alignbydevice/IoTDBAlignByDeviceIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/db/it/alignbydevice/IoTDBAlignByDeviceIT.java
@@ -100,6 +100,7 @@ public class IoTDBAlignByDeviceIT {
"insert into root.vehicle.d1(timestamp,s0) values(1,999)",
"insert into root.vehicle.d1(timestamp,s0) values(1000,888)",
"insert into root.other.d1(timestamp,s0) values(2, 3.14)",
+ "insert into root.other.d2(timestamp,s6) values(6, 6.66)",
};
@BeforeClass
@@ -1184,4 +1185,48 @@ public class IoTDBAlignByDeviceIT {
+ e.getMessage());
}
}
+
+ @Test
+ public void nonExistMeasurementInHavingTest() {
+ String[] retArray =
+ new String[] {
+ "1,root.other.d1,3.14,null,", "5,root.other.d2,null,6.66,",
+ };
+
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+
+ try (ResultSet resultSet =
+ statement.executeQuery(
+ "select last_value(s0),last_value(s6) from root.other.** group
by ([1,10),2ms) having last_value(s0) is not null or last_value(s6) is not null
align by device")) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ List<Integer> actualIndexToExpectedIndexList =
+ checkHeader(
+ resultSetMetaData,
+ "Time,Device,last_value(s0),last_value(s6)",
+ new int[] {
+ Types.TIMESTAMP, Types.VARCHAR, Types.FLOAT, Types.DOUBLE,
+ });
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ String[] expectedStrings = retArray[cnt].split(",");
+ StringBuilder expectedBuilder = new StringBuilder();
+ StringBuilder actualBuilder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ actualBuilder.append(resultSet.getString(i)).append(",");
+ expectedBuilder
+ .append(expectedStrings[actualIndexToExpectedIndexList.get(i -
1)])
+ .append(",");
+ }
+ Assert.assertEquals(expectedBuilder.toString(),
actualBuilder.toString());
+ cnt++;
+ }
+ Assert.assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
}
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..b855603bd9c 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,7 @@ 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.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 +1076,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()
@@ -1088,6 +1089,10 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
for (Expression aggregationExpression :
searchAggregationExpressions(expression)) {
Expression normalizedAggregationExpression =
normalizeExpression(aggregationExpression);
+ if (!new
ExistUnknownTypeInExpression().process(aggregationExpression, null).isEmpty()) {
+ continue;
+ }
+
analyzeExpressionType(analysis, aggregationExpression);
analyzeExpressionType(analysis, normalizedAggregationExpression);
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/expression/visitor/ColumnTransformerVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
index 498cac300fb..4c15193fd5f 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,19 @@ 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();
+ }
+ }
+ throw new IllegalStateException(
+ String.format(
+ "Unknown expression type: %s, perhaps it has non existent
measurement.",
+ expression.getOutputSymbol()));
+ }
}
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..d786e48dd00
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ExistUnknownTypeInExpression.java
@@ -0,0 +1,60 @@
+/*
+ * 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) {
+ 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/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..79fdd754f27
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/cartesian/ConcatDeviceAndBindSchemaForHavingVisitor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.singletonList(
+ new TimeSeriesOperand(new MeasurementPath(concatPath,
TSDataType.UNKNOWN)));
+ }
+
+ 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;
+ }
+}