blob: 010cf8fcd01ac1173669dec1c38332cdde86a621 [file] [log] [blame]
/*
* 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.analyze;
import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.commons.udf.builtin.BuiltinScalarFunction;
import org.apache.iotdb.commons.udf.builtin.BuiltinTimeSeriesGeneratingFunction;
import org.apache.iotdb.db.exception.sql.SemanticException;
import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
import org.apache.iotdb.db.queryengine.common.schematree.ISchemaTree;
import org.apache.iotdb.db.queryengine.plan.expression.Expression;
import org.apache.iotdb.db.queryengine.plan.expression.UnknownExpressionTypeException;
import org.apache.iotdb.db.queryengine.plan.expression.binary.BinaryExpression;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.LeafOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.NullOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
import org.apache.iotdb.db.queryengine.plan.expression.other.CaseWhenThenExpression;
import org.apache.iotdb.db.queryengine.plan.expression.ternary.TernaryExpression;
import org.apache.iotdb.db.queryengine.plan.expression.unary.UnaryExpression;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.BindTypeForTimeSeriesOperandVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.CollectAggregationExpressionsVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.CollectSourceExpressionsVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.ExpressionNormalizeVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.GetMeasurementExpressionVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.LowercaseNormalizeVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.ReplaceRawPathWithGroupedPathVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.ReplaceSubTreeWithViewVisitor;
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.ConcatDeviceAndBindSchemaForPredicateVisitor;
import org.apache.iotdb.db.queryengine.plan.expression.visitor.cartesian.ConcatExpressionWithSuffixPathsVisitor;
import org.apache.iotdb.db.queryengine.plan.statement.component.ResultColumn;
import org.apache.iotdb.db.utils.constant.SqlConstant;
import org.apache.tsfile.common.constant.TsFileConstant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class ExpressionAnalyzer {
private static final String RAW_AGGREGATION_HYBRID_ERROR_MSG =
"Raw data and aggregation result hybrid calculation is not supported.";
private static final String CONSTANT_COLUMN_ERROR_MSG = "Constant column is not supported.";
private ExpressionAnalyzer() {
// forbidden construction
}
/**
* Check if all suffix paths in expression are measurements or one-level wildcards, used in ALIGN
* BY DEVICE query or GroupByLevel query. If not, throw a {@link SemanticException}.
*
* @param expression expression to be checked
*/
public static void checkIsAllMeasurement(Expression expression) {
if (expression instanceof TernaryExpression) {
checkIsAllMeasurement(((TernaryExpression) expression).getFirstExpression());
checkIsAllMeasurement(((TernaryExpression) expression).getSecondExpression());
checkIsAllMeasurement(((TernaryExpression) expression).getThirdExpression());
} else if (expression instanceof BinaryExpression) {
checkIsAllMeasurement(((BinaryExpression) expression).getLeftExpression());
checkIsAllMeasurement(((BinaryExpression) expression).getRightExpression());
} else if (expression instanceof UnaryExpression) {
checkIsAllMeasurement(((UnaryExpression) expression).getExpression());
} else if (expression instanceof FunctionExpression) {
for (Expression childExpression : expression.getExpressions()) {
checkIsAllMeasurement(childExpression);
}
} else if (expression instanceof CaseWhenThenExpression) {
for (Expression childExpression : expression.getExpressions()) {
checkIsAllMeasurement(childExpression);
}
} else if (expression instanceof TimeSeriesOperand) {
PartialPath path = ((TimeSeriesOperand) expression).getPath();
if (path.getNodes().length > 1
|| path.getFullPath().equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) {
throw new SemanticException(
"the suffix paths can only be measurement or one-level wildcard");
}
} else if (expression instanceof TimestampOperand
|| expression instanceof ConstantOperand
|| expression instanceof NullOperand) {
// do nothing
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
public static ResultColumn.ColumnType identifyOutputColumnType(
Expression expression, boolean isRoot) {
if (expression instanceof TernaryExpression) {
ResultColumn.ColumnType firstType =
identifyOutputColumnType(((TernaryExpression) expression).getFirstExpression(), false);
ResultColumn.ColumnType secondType =
identifyOutputColumnType(((TernaryExpression) expression).getSecondExpression(), false);
ResultColumn.ColumnType thirdType =
identifyOutputColumnType(((TernaryExpression) expression).getThirdExpression(), false);
boolean rawFlag = false;
boolean aggregationFlag = false;
if (firstType == ResultColumn.ColumnType.RAW
|| secondType == ResultColumn.ColumnType.RAW
|| thirdType == ResultColumn.ColumnType.RAW) {
rawFlag = true;
}
if (firstType == ResultColumn.ColumnType.AGGREGATION
|| secondType == ResultColumn.ColumnType.AGGREGATION
|| thirdType == ResultColumn.ColumnType.AGGREGATION) {
aggregationFlag = true;
}
if (rawFlag && aggregationFlag) {
throw new SemanticException(RAW_AGGREGATION_HYBRID_ERROR_MSG);
}
if (firstType == ResultColumn.ColumnType.CONSTANT
&& secondType == ResultColumn.ColumnType.CONSTANT
&& thirdType == ResultColumn.ColumnType.CONSTANT) {
throw new SemanticException(CONSTANT_COLUMN_ERROR_MSG);
}
if (firstType != ResultColumn.ColumnType.CONSTANT) {
return firstType;
}
if (secondType != ResultColumn.ColumnType.CONSTANT) {
return secondType;
}
return thirdType;
} else if (expression instanceof BinaryExpression) {
ResultColumn.ColumnType leftType =
identifyOutputColumnType(((BinaryExpression) expression).getLeftExpression(), false);
ResultColumn.ColumnType rightType =
identifyOutputColumnType(((BinaryExpression) expression).getRightExpression(), false);
if ((leftType == ResultColumn.ColumnType.RAW
&& rightType == ResultColumn.ColumnType.AGGREGATION)
|| (leftType == ResultColumn.ColumnType.AGGREGATION
&& rightType == ResultColumn.ColumnType.RAW)) {
throw new SemanticException(RAW_AGGREGATION_HYBRID_ERROR_MSG);
}
if (isRoot
&& leftType == ResultColumn.ColumnType.CONSTANT
&& rightType == ResultColumn.ColumnType.CONSTANT) {
throw new SemanticException(CONSTANT_COLUMN_ERROR_MSG);
}
if (leftType != ResultColumn.ColumnType.CONSTANT) {
return leftType;
}
return rightType;
} else if (expression instanceof UnaryExpression) {
return identifyOutputColumnType(((UnaryExpression) expression).getExpression(), false);
} else if (expression instanceof FunctionExpression) {
List<Expression> inputExpressions = expression.getExpressions();
if (expression.isAggregationFunctionExpression()) {
for (Expression inputExpression : inputExpressions) {
if (identifyOutputColumnType(inputExpression, false)
== ResultColumn.ColumnType.AGGREGATION) {
throw new SemanticException(
"Aggregation results cannot be as input of the aggregation function.");
}
}
return ResultColumn.ColumnType.AGGREGATION;
} else {
ResultColumn.ColumnType checkedType = null;
int lastCheckedIndex = 0;
for (int i = 0; i < inputExpressions.size(); i++) {
ResultColumn.ColumnType columnType =
identifyOutputColumnType(inputExpressions.get(i), false);
if (columnType != ResultColumn.ColumnType.CONSTANT) {
checkedType = columnType;
lastCheckedIndex = i;
break;
}
}
if (checkedType == null) {
throw new SemanticException(
String.format(
"Input of '%s' is illegal.",
((FunctionExpression) expression).getFunctionName()));
}
for (int i = lastCheckedIndex; i < inputExpressions.size(); i++) {
ResultColumn.ColumnType columnType =
identifyOutputColumnType(inputExpressions.get(i), false);
if (columnType != ResultColumn.ColumnType.CONSTANT && columnType != checkedType) {
throw new SemanticException(
String.format(
"Raw data and aggregation result hybrid input of '%s' is not supported.",
((FunctionExpression) expression).getFunctionName()));
}
}
return checkedType;
}
} else if (expression instanceof CaseWhenThenExpression) {
// first, get all subexpression's type
CaseWhenThenExpression caseExpression = (CaseWhenThenExpression) expression;
List<ResultColumn.ColumnType> typeList =
caseExpression.getExpressions().stream()
.map(e -> identifyOutputColumnType(e, false))
.collect(Collectors.toList());
// if at least one subexpression is RAW, I'm RAW too
boolean rawFlag =
typeList.stream().anyMatch(columnType -> columnType == ResultColumn.ColumnType.RAW);
// if at least one subexpression is AGGREGATION, I'm AGGREGATION too
boolean aggregationFlag =
typeList.stream()
.anyMatch(columnType -> columnType == ResultColumn.ColumnType.AGGREGATION);
// not allow RAW && AGGREGATION
if (rawFlag && aggregationFlag) {
throw new SemanticException(RAW_AGGREGATION_HYBRID_ERROR_MSG);
}
// not allow all const
boolean allConst =
typeList.stream().allMatch(columnType -> columnType == ResultColumn.ColumnType.CONSTANT);
if (allConst) {
throw new SemanticException(CONSTANT_COLUMN_ERROR_MSG);
}
for (ResultColumn.ColumnType type : typeList) {
if (type != ResultColumn.ColumnType.CONSTANT) {
return type;
}
}
throw new IllegalArgumentException("shouldn't attach here");
} else if (expression instanceof TimeSeriesOperand || expression instanceof TimestampOperand) {
return ResultColumn.ColumnType.RAW;
} else if (expression instanceof ConstantOperand || expression instanceof NullOperand) {
return ResultColumn.ColumnType.CONSTANT;
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
/**
* Concat suffix path in SELECT or WITHOUT NULL clause with the prefix path in the FROM clause.
* and Construct a {@link PathPatternTree}.
*
* @param expression expression in SELECT or WITHOUT NULL clause which may include suffix paths
* @param prefixPaths prefix paths in the FROM clause
* @param patternTree a PathPatternTree contains all paths to query
* @return the concatenated expression list
*/
public static List<Expression> concatExpressionWithSuffixPaths(
Expression expression, List<PartialPath> prefixPaths, PathPatternTree patternTree) {
return new ConcatExpressionWithSuffixPathsVisitor()
.process(
expression,
new ConcatExpressionWithSuffixPathsVisitor.Context(prefixPaths, patternTree));
}
/**
* Concat suffix path in SELECT or WITHOUT NULL clause with the prefix path in the FROM clause.
*
* @param expression expression in SELECT or WITHOUT NULL clause which may include suffix paths
* @param prefixPaths prefix paths in the FROM clause
* @return the concatenated partialPath list
*/
public static List<PartialPath> concatExpressionWithSuffixPaths(
Expression expression, List<PartialPath> prefixPaths) {
Set<PartialPath> resultPaths = new HashSet<>();
if (expression instanceof TernaryExpression) {
List<PartialPath> firstExpressions =
concatExpressionWithSuffixPaths(
((TernaryExpression) expression).getFirstExpression(), prefixPaths);
List<PartialPath> secondExpressions =
concatExpressionWithSuffixPaths(
((TernaryExpression) expression).getSecondExpression(), prefixPaths);
List<PartialPath> thirdExpressions =
concatExpressionWithSuffixPaths(
((TernaryExpression) expression).getThirdExpression(), prefixPaths);
resultPaths.addAll(firstExpressions);
resultPaths.addAll(secondExpressions);
resultPaths.addAll(thirdExpressions);
return new ArrayList<>(resultPaths);
} else if (expression instanceof BinaryExpression) {
List<PartialPath> leftExpressions =
concatExpressionWithSuffixPaths(
((BinaryExpression) expression).getLeftExpression(), prefixPaths);
List<PartialPath> rightExpressions =
concatExpressionWithSuffixPaths(
((BinaryExpression) expression).getRightExpression(), prefixPaths);
resultPaths.addAll(leftExpressions);
resultPaths.addAll(rightExpressions);
return new ArrayList<>(resultPaths);
} else if (expression instanceof UnaryExpression) {
List<PartialPath> childExpressions =
concatExpressionWithSuffixPaths(
((UnaryExpression) expression).getExpression(), prefixPaths);
resultPaths.addAll(childExpressions);
return new ArrayList<>(resultPaths);
} else if (expression instanceof FunctionExpression) {
for (Expression suffixExpression : expression.getExpressions()) {
resultPaths.addAll(concatExpressionWithSuffixPaths(suffixExpression, prefixPaths));
}
return new ArrayList<>(resultPaths);
} else if (expression instanceof TimeSeriesOperand) {
PartialPath rawPath = ((TimeSeriesOperand) expression).getPath();
List<PartialPath> actualPaths = new ArrayList<>();
if (rawPath.getFullPath().startsWith(SqlConstant.ROOT + TsFileConstant.PATH_SEPARATOR)) {
actualPaths.add(rawPath);
} else {
for (PartialPath prefixPath : prefixPaths) {
PartialPath concatPath = prefixPath.concatPath(rawPath);
actualPaths.add(concatPath);
}
}
return actualPaths;
} else if (expression instanceof CaseWhenThenExpression) {
return expression.getExpressions().stream()
.map(expression1 -> concatExpressionWithSuffixPaths(expression1, prefixPaths))
.flatMap(Collection::stream)
.collect(Collectors.toList());
} else if (expression instanceof TimestampOperand
|| expression instanceof ConstantOperand
|| expression instanceof NullOperand) {
return new ArrayList<>();
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
/**
* Concat suffix path in WHERE clause with the prefix path in the FROM clause and Construct a
* {@link PathPatternTree}. This method return void, i.e. will not rewrite the statement.
*
* @param predicate expression in WHERE clause
* @param prefixPaths prefix paths in the FROM clause
* @param patternTree a PathPatternTree contains all paths to query
*/
public static void constructPatternTreeFromExpression(
Expression predicate, List<PartialPath> prefixPaths, PathPatternTree patternTree) {
if (predicate instanceof TernaryExpression) {
constructPatternTreeFromExpression(
((TernaryExpression) predicate).getFirstExpression(), prefixPaths, patternTree);
constructPatternTreeFromExpression(
((TernaryExpression) predicate).getSecondExpression(), prefixPaths, patternTree);
constructPatternTreeFromExpression(
((TernaryExpression) predicate).getThirdExpression(), prefixPaths, patternTree);
} else if (predicate instanceof BinaryExpression) {
constructPatternTreeFromExpression(
((BinaryExpression) predicate).getLeftExpression(), prefixPaths, patternTree);
constructPatternTreeFromExpression(
((BinaryExpression) predicate).getRightExpression(), prefixPaths, patternTree);
} else if (predicate instanceof UnaryExpression) {
constructPatternTreeFromExpression(
((UnaryExpression) predicate).getExpression(), prefixPaths, patternTree);
} else if (predicate instanceof FunctionExpression) {
for (Expression suffixExpression : predicate.getExpressions()) {
constructPatternTreeFromExpression(suffixExpression, prefixPaths, patternTree);
}
} else if (predicate instanceof TimeSeriesOperand) {
PartialPath rawPath = ((TimeSeriesOperand) predicate).getPath();
if (rawPath.getFullPath().startsWith(SqlConstant.ROOT + TsFileConstant.PATH_SEPARATOR)) {
patternTree.appendPathPattern(rawPath);
return;
}
for (PartialPath prefixPath : prefixPaths) {
PartialPath concatPath = prefixPath.concatPath(rawPath);
patternTree.appendPathPattern(concatPath);
}
} else if (predicate instanceof CaseWhenThenExpression) {
predicate
.getExpressions()
.forEach(
expression ->
constructPatternTreeFromExpression(expression, prefixPaths, patternTree));
} else if (predicate instanceof TimestampOperand
|| predicate instanceof ConstantOperand
|| predicate instanceof NullOperand) {
// do nothing
} else {
throw new UnknownExpressionTypeException(predicate.getExpressionType());
}
}
/**
* Bind schema ({@link PartialPath} -> {@link MeasurementPath}) and removes wildcards in
* Expression. And all logical view will be replaced.
*
* @param schemaTree interface for querying schema information
* @return the expression list after binding schema and whether there is logical view in
* expressions
*/
public static List<Expression> bindSchemaForExpression(
Expression expression, ISchemaTree schemaTree) {
return new BindSchemaForExpressionVisitor().process(expression, schemaTree);
}
/**
* Concat suffix path in WHERE and HAVING clause with the prefix path in the FROM clause. And
* then, bind schema ({@link PartialPath} -> {@link MeasurementPath}) and removes wildcards in
* Expression. Logical view will be replaced.
*
* @param prefixPaths prefix paths in the FROM clause
* @param schemaTree interface for querying schema information
* @return the expression list with full path and after binding schema
*/
public static List<Expression> bindSchemaForPredicate(
Expression predicate, List<PartialPath> prefixPaths, ISchemaTree schemaTree, boolean isRoot) {
return new BindSchemaForPredicateVisitor()
.process(
predicate, new BindSchemaForPredicateVisitor.Context(prefixPaths, schemaTree, isRoot));
}
public static Expression replaceRawPathWithGroupedPath(
Expression expression,
GroupByLevelHelper.RawPathToGroupedPathMap rawPathToGroupedPathMap,
UnaryOperator<PartialPath> pathTransformer) {
return new ReplaceRawPathWithGroupedPathVisitor()
.process(
expression,
new ReplaceRawPathWithGroupedPathVisitor.Context(
rawPathToGroupedPathMap, pathTransformer));
}
/**
* Concat expression with the device path in the FROM clause.And then, bind schema ({@link
* PartialPath} -> {@link MeasurementPath}) and removes wildcards in Expression. This method used
* in ALIGN BY DEVICE query.
*
* @param devicePath device path in the FROM clause
* @return expression list with full path and after binding schema
*/
public static List<Expression> concatDeviceAndBindSchemaForExpression(
Expression expression, PartialPath devicePath, ISchemaTree schemaTree) {
return new ConcatDeviceAndBindSchemaForExpressionVisitor()
.process(
expression,
new ConcatDeviceAndBindSchemaForExpressionVisitor.Context(devicePath, schemaTree));
}
/**
* Concat measurement in WHERE and HAVING clause with device path. And then, bind schema ({@link
* PartialPath} -> {@link MeasurementPath}) and removes wildcards.
*
* @return the expression list with full path and after binding schema
*/
public static List<Expression> concatDeviceAndBindSchemaForPredicate(
Expression predicate, PartialPath devicePath, ISchemaTree schemaTree, boolean isWhere) {
return new ConcatDeviceAndBindSchemaForPredicateVisitor()
.process(
predicate,
new ConcatDeviceAndBindSchemaForPredicateVisitor.Context(
devicePath, schemaTree, isWhere));
}
/**
* Search for subexpressions that can be queried natively, including all time series.
*
* @param expression expression to be searched
* @return searched subexpression list
*/
public static List<Expression> searchSourceExpressions(Expression expression) {
return new CollectSourceExpressionsVisitor().process(expression, null);
}
public static Expression replaceSubTreeWithView(Expression expression, Analysis analysis) {
return new ReplaceSubTreeWithViewVisitor().process(expression, analysis);
}
/**
* Search for built-in aggregate functions subexpressions.
*
* @param expression expression to be searched
* @return searched aggregate functions list
*/
public static List<Expression> searchAggregationExpressions(Expression expression) {
return new CollectAggregationExpressionsVisitor().process(expression, null);
}
/**
* Remove view and measurement alias from expression, and reconstruct the name of
* FunctionExpression to lowercase.
*
* @return normalized expression
*/
public static Expression normalizeExpression(Expression expression) {
return new ExpressionNormalizeVisitor().process(expression, null);
}
/**
* Reconstruct the name of FunctionExpression to lowercase.
*
* @return normalized expression
*/
public static Expression toLowerCaseExpression(Expression expression) {
return new LowercaseNormalizeVisitor().process(expression, null);
}
/** Check for arithmetic expression, logical expression, UDTF. Returns true if it exists. */
public static boolean checkIsNeedTransform(Expression expression) {
if (expression instanceof TernaryExpression) {
return true;
} else if (expression instanceof BinaryExpression) {
return true;
} else if (expression instanceof UnaryExpression) {
return true;
} else if (expression instanceof FunctionExpression) {
return !expression.isAggregationFunctionExpression();
} else if (expression instanceof TimeSeriesOperand) {
return false;
} else if (expression instanceof ConstantOperand) {
return false;
} else if (expression instanceof NullOperand) {
return true;
} else if (expression instanceof CaseWhenThenExpression) {
return true;
} else if (expression instanceof TimestampOperand) {
return false;
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Method can only be used in source expression
/////////////////////////////////////////////////////////////////////////////////////////////////
public static String getDeviceNameInSourceExpression(Expression expression) {
if (!(expression instanceof TimeSeriesOperand)) {
throw new IllegalArgumentException(
"unsupported expression type for source expression: " + expression.getExpressionType());
}
return ((TimeSeriesOperand) expression).getPath().getDevice();
}
public static Expression getMeasurementExpression(Expression expression, Analysis analysis) {
return new GetMeasurementExpressionVisitor().process(expression, analysis);
}
/**
* Bind DataType for TimeSeriesOperand in Expression with according ColumnHeaderName. eg:
*
* <p>columnHeaders: [[QueryId, TEXT], [DataNodeId, INT32]...]
*
* <p>dataNodeID > 1 -> DataNodeId > 1, `DataNodeID` will be a MeasurementPath with INT32
*
* <p>errorInput > 1, no according ColumnHeaderName of `errorInput`, throw exception
*/
public static Expression bindTypeForTimeSeriesOperand(
Expression predicate, List<ColumnHeader> columnHeaders) {
return new BindTypeForTimeSeriesOperandVisitor().process(predicate, columnHeaders);
}
public static boolean isDeviceViewNeedSpecialProcess(Expression expression, Analysis analysis) {
if (expression instanceof TernaryExpression) {
TernaryExpression ternaryExpression = (TernaryExpression) expression;
return isDeviceViewNeedSpecialProcess(ternaryExpression.getFirstExpression(), analysis)
|| isDeviceViewNeedSpecialProcess(ternaryExpression.getSecondExpression(), analysis)
|| isDeviceViewNeedSpecialProcess(ternaryExpression.getThirdExpression(), analysis);
} else if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
return isDeviceViewNeedSpecialProcess(binaryExpression.getLeftExpression(), analysis)
|| isDeviceViewNeedSpecialProcess(binaryExpression.getRightExpression(), analysis);
} else if (expression instanceof UnaryExpression) {
return isDeviceViewNeedSpecialProcess(
((UnaryExpression) expression).getExpression(), analysis);
} else if (expression instanceof FunctionExpression) {
String functionName = ((FunctionExpression) expression).getFunctionName().toLowerCase();
if (!expression.isMappable(analysis.getExpressionTypes())
|| BuiltinScalarFunction.DEVICE_VIEW_SPECIAL_PROCESS_FUNCTIONS.contains(functionName)
|| BuiltinTimeSeriesGeneratingFunction.DEVICE_VIEW_SPECIAL_PROCESS_FUNCTIONS.contains(
functionName)) {
return true;
}
for (Expression child : expression.getExpressions()) {
if (isDeviceViewNeedSpecialProcess(child, analysis)) {
return true;
}
}
return false;
} else if (expression instanceof CaseWhenThenExpression) {
for (Expression subexpression : expression.getExpressions()) {
if (isDeviceViewNeedSpecialProcess(subexpression, analysis)) {
return true;
}
}
return false;
} else if (expression instanceof LeafOperand) {
return false;
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
public static boolean checkIsScalarExpression(Expression expression, Analysis analysis) {
if (expression instanceof TernaryExpression) {
TernaryExpression ternaryExpression = (TernaryExpression) expression;
return checkIsScalarExpression(ternaryExpression.getFirstExpression(), analysis)
&& checkIsScalarExpression(ternaryExpression.getSecondExpression(), analysis)
&& checkIsScalarExpression(ternaryExpression.getThirdExpression(), analysis);
} else if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
return checkIsScalarExpression(binaryExpression.getLeftExpression(), analysis)
&& checkIsScalarExpression(binaryExpression.getRightExpression(), analysis);
} else if (expression instanceof UnaryExpression) {
return checkIsScalarExpression(((UnaryExpression) expression).getExpression(), analysis);
} else if (expression instanceof FunctionExpression) {
FunctionExpression functionExpression = (FunctionExpression) expression;
if (!functionExpression.isMappable(analysis.getExpressionTypes())
|| BuiltinScalarFunction.DEVICE_VIEW_SPECIAL_PROCESS_FUNCTIONS.contains(
functionExpression.getFunctionName())) {
return false;
}
List<Expression> inputExpressions = functionExpression.getExpressions();
for (Expression inputExpression : inputExpressions) {
if (!checkIsScalarExpression(inputExpression, analysis)) {
return false;
}
}
return true;
} else if (expression instanceof CaseWhenThenExpression) {
for (Expression subexpression : expression.getExpressions()) {
if (!checkIsScalarExpression(subexpression, analysis)) {
return false;
}
}
return true;
} else if (expression instanceof LeafOperand) {
return true;
} else {
throw new UnknownExpressionTypeException(expression.getExpressionType());
}
}
}