blob: 34363703422c6e7605eefdd6cdf58bbcc40cfb47 [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.mpp.plan.expression;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.exception.query.LogicalOptimizeException;
import org.apache.iotdb.db.exception.query.QueryProcessException;
import org.apache.iotdb.db.exception.sql.SemanticException;
import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider;
import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression;
import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression;
import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression;
import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor;
import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation;
import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer;
import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer;
import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner;
import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext;
import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor;
import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** A skeleton class for expression */
public abstract class Expression {
/////////////////////////////////////////////////////////////////////////////////////////////////
// Operations that Class Expression is not responsible for should be done through a visitor ////
/////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Accessible for {@link ExpressionVisitor}, use {@link ExpressionVisitor#process(Expression)}
* instead.
*/
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitExpression(this, context);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Expression type inferring for execution plan generation //////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
public abstract ExpressionType getExpressionType();
public boolean isBuiltInAggregationFunctionExpression() {
return false;
}
public boolean isUserDefinedAggregationFunctionExpression() {
return false;
}
public boolean isTimeSeriesGeneratingFunctionExpression() {
return false;
}
public boolean isCompareBinaryExpression() {
return false;
}
public abstract boolean isMappable(TypeProvider typeProvider);
/////////////////////////////////////////////////////////////////////////////////////////////////
// isConstantOperand
/////////////////////////////////////////////////////////////////////////////////////////////////
protected Boolean isConstantOperandCache = null;
/** If this expression and all of its sub-expressions are {@link ConstantOperand}. */
public final boolean isConstantOperand() {
if (isConstantOperandCache == null) {
isConstantOperandCache = isConstantOperandInternal();
}
return isConstantOperandCache;
}
/** Sub-classes should override this method indicating if the expression is a constant operand */
protected abstract boolean isConstantOperandInternal();
/////////////////////////////////////////////////////////////////////////////////////////////////
// Operations for time series paths
/////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: remove after MPP finish
@Deprecated
public abstract void concat(List<PartialPath> prefixPaths, List<Expression> resultExpressions);
// TODO: remove after MPP finish
@Deprecated
public abstract void removeWildcards(
org.apache.iotdb.db.qp.utils.WildcardsRemover wildcardsRemover,
List<Expression> resultExpressions)
throws LogicalOptimizeException;
// TODO: remove after MPP finish
@Deprecated
public abstract void collectPaths(Set<PartialPath> pathSet);
/////////////////////////////////////////////////////////////////////////////////////////////////
// For UDF instances initialization
/////////////////////////////////////////////////////////////////////////////////////////////////
public abstract void constructUdfExecutors(
Map<String, UDTFExecutor> expressionName2Executor, ZoneId zoneId);
/////////////////////////////////////////////////////////////////////////////////////////////////
// type inference
/////////////////////////////////////////////////////////////////////////////////////////////////
public abstract TSDataType inferTypes(TypeProvider typeProvider);
protected static void checkInputExpressionDataType(
String expressionString, TSDataType actual, TSDataType... expected) {
for (TSDataType type : expected) {
if (actual.equals(type)) {
return;
}
}
throw new SemanticException(
String.format(
"Invalid input expression data type. expression: %s, actual data type: %s, expected data type(s): %s.",
expressionString, actual.name(), Arrays.toString(expected)));
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// For expression evaluation DAG building
/////////////////////////////////////////////////////////////////////////////////////////////////
protected Integer inputColumnIndex = null;
// TODO: remove after MPP finish
@Deprecated
public abstract void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan);
public abstract void bindInputLayerColumnIndexWithExpression(
Map<String, List<InputLocation>> inputLocations);
public abstract void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner);
// TODO: remove after MPP finish
@Deprecated
public abstract IntermediateLayer constructIntermediateLayer(
long queryId,
UDTFContext udtfContext,
QueryDataSetInputLayer rawTimeSeriesInputLayer,
Map<Expression, IntermediateLayer> expressionIntermediateLayerMap,
Map<Expression, TSDataType> expressionDataTypeMap,
LayerMemoryAssigner memoryAssigner)
throws QueryProcessException, IOException;
public abstract IntermediateLayer constructIntermediateLayer(
long queryId,
UDTFContext udtfContext,
QueryDataSetInputLayer rawTimeSeriesInputLayer,
Map<Expression, IntermediateLayer> expressionIntermediateLayerMap,
TypeProvider typeProvider,
LayerMemoryAssigner memoryAssigner)
throws QueryProcessException, IOException;
/////////////////////////////////////////////////////////////////////////////////////////////////
// toString
/////////////////////////////////////////////////////////////////////////////////////////////////
private String expressionStringCache;
/** Sub-classes must not override this method. */
@Override
public final String toString() {
return getExpressionString();
}
/**
* Get the representation of the expression in string. The hash code of the returned value will be
* the hash code of this object. See {@link #hashCode()} and {@link #equals(Object)}. In other
* words, same expressions should have exactly the same string representation, and different
* expressions must have different string representations.
*/
public final String getExpressionString() {
if (expressionStringCache == null) {
expressionStringCache = getExpressionStringInternal();
}
return expressionStringCache;
}
/**
* Sub-classes should override this method to provide valid string representation of this object.
* See {@link #getExpressionString()}
*/
protected abstract String getExpressionStringInternal();
/////////////////////////////////////////////////////////////////////////////////////////////////
// hashCode & equals
/////////////////////////////////////////////////////////////////////////////////////////////////
/** Sub-classes must not override this method. */
@Override
public final int hashCode() {
return getExpressionString().hashCode();
}
/** Sub-classes must not override this method. */
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Expression)) {
return false;
}
return getExpressionString().equals(((Expression) o).getExpressionString());
}
/**
* returns the DIRECT children expressions if it has any, otherwise an EMPTY list will be returned
*/
public abstract List<Expression> getExpressions();
/////////////////////////////////////////////////////////////////////////////////////////////////
// serialize & deserialize
/////////////////////////////////////////////////////////////////////////////////////////////////
public static void serialize(Expression expression, ByteBuffer byteBuffer) {
ReadWriteIOUtils.write(
expression.getExpressionType().getExpressionTypeInShortEnum(), byteBuffer);
expression.serialize(byteBuffer);
ReadWriteIOUtils.write(expression.inputColumnIndex != null, byteBuffer);
if (expression.inputColumnIndex != null) {
ReadWriteIOUtils.write(expression.inputColumnIndex, byteBuffer);
}
}
public static void serialize(Expression expression, DataOutputStream stream) throws IOException {
ReadWriteIOUtils.write(expression.getExpressionType().getExpressionTypeInShortEnum(), stream);
expression.serialize(stream);
ReadWriteIOUtils.write(expression.inputColumnIndex != null, stream);
if (expression.inputColumnIndex != null) {
ReadWriteIOUtils.write(expression.inputColumnIndex, stream);
}
}
public static Expression deserialize(ByteBuffer byteBuffer) {
short type = ReadWriteIOUtils.readShort(byteBuffer);
Expression expression;
switch (type) {
case -4:
expression = new ConstantOperand(byteBuffer);
break;
case -3:
expression = new TimestampOperand(byteBuffer);
break;
case -2:
expression = new TimeSeriesOperand(byteBuffer);
break;
case -1:
expression = new FunctionExpression(byteBuffer);
break;
case 0:
expression = new NegationExpression(byteBuffer);
break;
case 1:
expression = new LogicNotExpression(byteBuffer);
break;
case 2:
expression = new MultiplicationExpression(byteBuffer);
break;
case 3:
expression = new DivisionExpression(byteBuffer);
break;
case 4:
expression = new ModuloExpression(byteBuffer);
break;
case 5:
expression = new AdditionExpression(byteBuffer);
break;
case 6:
expression = new SubtractionExpression(byteBuffer);
break;
case 7:
expression = new EqualToExpression(byteBuffer);
break;
case 8:
expression = new NonEqualExpression(byteBuffer);
break;
case 9:
expression = new GreaterEqualExpression(byteBuffer);
break;
case 10:
expression = new GreaterThanExpression(byteBuffer);
break;
case 11:
expression = new LessEqualExpression(byteBuffer);
break;
case 12:
expression = new LessThanExpression(byteBuffer);
break;
case 13:
expression = new LikeExpression(byteBuffer);
break;
case 14:
expression = new RegularExpression(byteBuffer);
break;
case 15:
expression = new IsNullExpression(byteBuffer);
break;
case 16:
expression = new BetweenExpression(byteBuffer);
break;
case 17:
expression = new InExpression(byteBuffer);
break;
case 18:
expression = new LogicAndExpression(byteBuffer);
break;
case 19:
expression = new LogicOrExpression(byteBuffer);
break;
default:
throw new IllegalArgumentException("Invalid expression type: " + type);
}
boolean hasInputColumnIndex = ReadWriteIOUtils.readBool(byteBuffer);
if (hasInputColumnIndex) {
expression.inputColumnIndex = ReadWriteIOUtils.readInt(byteBuffer);
}
return expression;
}
protected abstract void serialize(ByteBuffer byteBuffer);
protected abstract void serialize(DataOutputStream stream) throws IOException;
/////////////////////////////////////////////////////////////////////////////////////////////////
// iterator: level-order traversal iterator
/////////////////////////////////////////////////////////////////////////////////////////////////
/** returns an iterator to traverse all the successor expressions in a level-order */
public final Iterator<Expression> iterator() {
return new ExpressionIterator(this);
}
/** the iterator of an Expression tree with level-order traversal */
private static class ExpressionIterator implements Iterator<Expression> {
private final Deque<Expression> queue = new LinkedList<>();
public ExpressionIterator(Expression expression) {
queue.add(expression);
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
@Override
public Expression next() {
if (!hasNext()) {
return null;
}
Expression current = queue.pop();
if (current != null) {
for (Expression subExp : current.getExpressions()) {
queue.push(subExp);
}
}
return current;
}
}
}