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; + } +}
