This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch ty/TableModelGrammar in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit ae8e5fa6bbfd6dfbe32c7e0532fb6e4deaea6d04 Author: JackieTien97 <[email protected]> AuthorDate: Tue Apr 9 18:19:06 2024 +0800 add returnType impl --- .../plan/relational/analyzer/AnalyzerTest.java | 49 ++--- .../plan/relational/analyzer/TestMatadata.java | 206 ++++++++++++++++++++- 2 files changed, 227 insertions(+), 28 deletions(-) diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java index d2018ff4256..95436deea9e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java @@ -22,16 +22,12 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer; import org.apache.iotdb.db.queryengine.common.MPPQueryContext; import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.common.SessionInfo; -import org.apache.iotdb.db.queryengine.plan.relational.function.BoundSignature; -import org.apache.iotdb.db.queryengine.plan.relational.function.FunctionId; -import org.apache.iotdb.db.queryengine.plan.relational.function.FunctionKind; import org.apache.iotdb.db.queryengine.plan.relational.function.OperatorType; import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle; import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnSchema; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.metadata.OperatorNotFoundException; import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; -import org.apache.iotdb.db.queryengine.plan.relational.metadata.ResolvedFunction; import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle; import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableSchema; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; @@ -51,6 +47,7 @@ import java.util.Optional; import static org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector.NOOP; import static org.apache.iotdb.tsfile.read.common.type.BooleanType.BOOLEAN; +import static org.apache.iotdb.tsfile.read.common.type.DoubleType.DOUBLE; import static org.apache.iotdb.tsfile.read.common.type.IntType.INT32; import static org.apache.iotdb.tsfile.read.common.type.LongType.INT64; import static org.junit.Assert.assertNotNull; @@ -96,25 +93,35 @@ public class AnalyzerTest { Mockito.when(metadata.getTableSchema(Mockito.any(), eq(tableHandle))).thenReturn(tableSchema); Mockito.when(metadata.getColumnHandles(Mockito.any(), eq(tableHandle))).thenReturn(map); - ResolvedFunction lLessThanI = - new ResolvedFunction( - new BoundSignature("l<i", BOOLEAN, Arrays.asList(INT64, INT32)), - new FunctionId("l<i"), - FunctionKind.SCALAR, - true); - - ResolvedFunction iAddi = - new ResolvedFunction( - new BoundSignature("l+i", INT64, Arrays.asList(INT32, INT32)), - new FunctionId("l+i"), - FunctionKind.SCALAR, - true); + // ResolvedFunction lLessThanI = + // new ResolvedFunction( + // new BoundSignature("l<i", BOOLEAN, Arrays.asList(INT64, INT32)), + // new FunctionId("l<i"), + // FunctionKind.SCALAR, + // true); + // + // ResolvedFunction iAddi = + // new ResolvedFunction( + // new BoundSignature("l+i", INT64, Arrays.asList(INT32, INT32)), + // new FunctionId("l+i"), + // FunctionKind.SCALAR, + // true); + // + // Mockito.when( + // metadata.resolveOperator(eq(OperatorType.LESS_THAN), eq(Arrays.asList(INT64, + // INT32)))) + // .thenReturn(lLessThanI); + // Mockito.when(metadata.resolveOperator(eq(OperatorType.ADD), eq(Arrays.asList(INT32, + // INT32)))) + // .thenReturn(iAddi); Mockito.when( - metadata.resolveOperator(eq(OperatorType.LESS_THAN), eq(Arrays.asList(INT64, INT32)))) - .thenReturn(lLessThanI); - Mockito.when(metadata.resolveOperator(eq(OperatorType.ADD), eq(Arrays.asList(INT32, INT32)))) - .thenReturn(iAddi); + metadata.getOperatorReturnType( + eq(OperatorType.LESS_THAN), eq(Arrays.asList(INT64, INT32)))) + .thenReturn(BOOLEAN); + Mockito.when( + metadata.getOperatorReturnType(eq(OperatorType.ADD), eq(Arrays.asList(INT32, INT32)))) + .thenReturn(DOUBLE); Analysis actualAnalysis = analyzeSQL(sql, metadata); assertNotNull(actualAnalysis); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java index 486f18c761b..993ed00ebda 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java @@ -1,5 +1,8 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer; +import org.apache.iotdb.commons.udf.builtin.BuiltinAggregationFunction; +import org.apache.iotdb.commons.udf.builtin.BuiltinScalarFunction; +import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.queryengine.common.SessionInfo; import org.apache.iotdb.db.queryengine.plan.relational.function.OperatorType; import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle; @@ -13,8 +16,11 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle; import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadata; import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableSchema; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.db.queryengine.plan.relational.type.TypeManager; import org.apache.iotdb.db.queryengine.plan.relational.type.TypeNotFoundException; import org.apache.iotdb.db.queryengine.plan.relational.type.TypeSignature; +import org.apache.iotdb.db.utils.constant.SqlConstant; import org.apache.iotdb.tsfile.read.common.type.BinaryType; import org.apache.iotdb.tsfile.read.common.type.DoubleType; import org.apache.iotdb.tsfile.read.common.type.Type; @@ -24,15 +30,22 @@ import org.mockito.Mockito; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; +import static org.apache.iotdb.tsfile.read.common.type.BinaryType.TEXT; +import static org.apache.iotdb.tsfile.read.common.type.BooleanType.BOOLEAN; +import static org.apache.iotdb.tsfile.read.common.type.DoubleType.DOUBLE; +import static org.apache.iotdb.tsfile.read.common.type.FloatType.FLOAT; import static org.apache.iotdb.tsfile.read.common.type.IntType.INT32; import static org.apache.iotdb.tsfile.read.common.type.LongType.INT64; public class TestMatadata implements Metadata { - public static final String DB1 = "db1"; + private final TypeManager typeManager = new InternalTypeManager(); + + public static final String DB1 = "testdb"; public static final String TABLE1 = "table1"; public static final String TIME = "time"; private static final String TAG1 = "tag1"; @@ -85,7 +98,7 @@ public class TestMatadata implements Metadata { @Override public Optional<TableHandle> getTableHandle(SessionInfo session, QualifiedObjectName name) { - return Optional.empty(); + return Optional.of(new TableHandle()); } @Override @@ -113,27 +126,206 @@ public class TestMatadata implements Metadata { @Override public Type getOperatorReturnType(OperatorType operatorType, List<? extends Type> argumentTypes) throws OperatorNotFoundException { - return null; + + switch (operatorType) { + case ADD: + case SUBTRACT: + case MULTIPLY: + case DIVIDE: + case MODULUS: + if (!isTwoNumericType(argumentTypes)) { + throw new OperatorNotFoundException( + operatorType, + argumentTypes, + new IllegalArgumentException("Should have two numeric operands.")); + } + return DOUBLE; + case NEGATION: + if (!isOneNumericType(argumentTypes)) { + throw new OperatorNotFoundException( + operatorType, + argumentTypes, + new IllegalArgumentException("Should have one numeric operands.")); + } + return DOUBLE; + case EQUAL: + case LESS_THAN: + case LESS_THAN_OR_EQUAL: + if (!isTwoTypeComparable(argumentTypes)) { + throw new OperatorNotFoundException( + operatorType, + argumentTypes, + new IllegalArgumentException("Should have two comparable operands.")); + } + return BOOLEAN; + default: + throw new OperatorNotFoundException( + operatorType, argumentTypes, new UnsupportedOperationException()); + } } @Override public Type getFunctionReturnType(String functionName, List<? extends Type> argumentTypes) { - return null; + + // builtin scalar function + if (BuiltinScalarFunction.DIFF.getFunctionName().equalsIgnoreCase(functionName) + || BuiltinScalarFunction.ROUND.getFunctionName().equalsIgnoreCase(functionName)) { + if (!isOneNumericType(argumentTypes)) { + throw new SemanticException( + "Scalar function" + + functionName.toLowerCase(Locale.ENGLISH) + + " only supports numeric data types [INT32, INT64, FLOAT, DOUBLE]"); + } + return DOUBLE; + } else if (BuiltinScalarFunction.REPLACE.getFunctionName().equalsIgnoreCase(functionName) + || BuiltinScalarFunction.SUBSTRING.getFunctionName().equalsIgnoreCase(functionName)) { + if (!isOneTextType(argumentTypes)) { + throw new SemanticException( + "Scalar function" + + functionName.toLowerCase(Locale.ENGLISH) + + " only supports text data type."); + } + return TEXT; + } + + // builtin aggregation function + // check argument type + switch (functionName.toLowerCase()) { + case SqlConstant.AVG: + case SqlConstant.SUM: + case SqlConstant.EXTREME: + case SqlConstant.MIN_VALUE: + case SqlConstant.MAX_VALUE: + case SqlConstant.STDDEV: + case SqlConstant.STDDEV_POP: + case SqlConstant.STDDEV_SAMP: + case SqlConstant.VARIANCE: + case SqlConstant.VAR_POP: + case SqlConstant.VAR_SAMP: + if (!isOneNumericType(argumentTypes)) { + throw new SemanticException( + String.format( + "Aggregate functions [%s] only support numeric data types [INT32, INT64, FLOAT, DOUBLE]", + functionName)); + } + break; + case SqlConstant.MIN_TIME: + case SqlConstant.MAX_TIME: + case SqlConstant.FIRST_VALUE: + case SqlConstant.LAST_VALUE: + case SqlConstant.TIME_DURATION: + case SqlConstant.MODE: + if (argumentTypes.size() != 1) { + throw new SemanticException( + String.format( + "Aggregate functions [%s] should only have one argument", functionName)); + } + break; + case SqlConstant.MAX_BY: + case SqlConstant.MIN_BY: + if (argumentTypes.size() != 2) { + throw new SemanticException( + String.format( + "Aggregate functions [%s] should only have two arguments", functionName)); + } else if (!argumentTypes.get(1).isOrderable()) { + throw new SemanticException( + String.format( + "Second argument of Aggregate functions [%s] should be orderable", functionName)); + } + + break; + case SqlConstant.COUNT: + break; + default: + // ignore + } + + // get return type + switch (functionName.toLowerCase()) { + case SqlConstant.MIN_TIME: + case SqlConstant.MAX_TIME: + case SqlConstant.COUNT: + case SqlConstant.TIME_DURATION: + return INT64; + case SqlConstant.MIN_VALUE: + case SqlConstant.LAST_VALUE: + case SqlConstant.FIRST_VALUE: + case SqlConstant.MAX_VALUE: + case SqlConstant.EXTREME: + case SqlConstant.MODE: + case SqlConstant.MAX_BY: + case SqlConstant.MIN_BY: + return argumentTypes.get(0); + case SqlConstant.AVG: + case SqlConstant.SUM: + case SqlConstant.STDDEV: + case SqlConstant.STDDEV_POP: + case SqlConstant.STDDEV_SAMP: + case SqlConstant.VARIANCE: + case SqlConstant.VAR_POP: + case SqlConstant.VAR_SAMP: + return DOUBLE; + default: + // ignore + } + + // TODO scalar UDF function + + // TODO UDAF + + throw new SemanticException("Unknown function: " + functionName); } @Override public boolean isAggregationFunction( SessionInfo session, String functionName, AccessControl accessControl) { - return false; + return BuiltinAggregationFunction.getNativeFunctionNames() + .contains(functionName.toLowerCase(Locale.ENGLISH)); } @Override public Type getType(TypeSignature signature) throws TypeNotFoundException { - return null; + return typeManager.getType(signature); } @Override public boolean canCoerce(Type from, Type to) { - return false; + return true; + } + + public static boolean isTwoNumericType(List<? extends Type> argumentTypes) { + return argumentTypes.size() == 2 + && isNumericType(argumentTypes.get(0)) + && isNumericType(argumentTypes.get(1)); + } + + public static boolean isOneNumericType(List<? extends Type> argumentTypes) { + return argumentTypes.size() == 1 && isNumericType(argumentTypes.get(0)); + } + + public static boolean isOneBooleanType(List<? extends Type> argumentTypes) { + return argumentTypes.size() == 1 && BOOLEAN.equals(argumentTypes.get(0)); + } + + public static boolean isOneTextType(List<? extends Type> argumentTypes) { + return argumentTypes.size() == 1 && TEXT.equals(argumentTypes.get(0)); + } + + public static boolean isNumericType(Type type) { + return DOUBLE.equals(type) || FLOAT.equals(type) || INT32.equals(type) || INT64.equals(type); + } + + public static boolean isTwoTypeComparable(List<? extends Type> argumentTypes) { + if (argumentTypes.size() != 2) { + return false; + } + Type left = argumentTypes.get(0); + Type right = argumentTypes.get(1); + if (left.equals(right)) { + return true; + } + + // Boolean type and Binary Type can not be compared with other types + return isNumericType(left) && isNumericType(right); } }
