ASTERIXDB-1631: let TypeComputeUtils handle input type ANY properly.
Change-Id: Ie6b3f0e9d9b4ddd9280e06f72a5ca30aa776315d
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1191
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-om/pom.xml b/asterixdb/asterix-om/pom.xml
index 60fa26c..72bf65e 100644
--- a/asterixdb/asterix-om/pom.xml
+++ b/asterixdb/asterix-om/pom.xml
@@ -74,5 +74,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.10.19</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
index 184dd84..a85d33b 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
@@ -68,7 +68,7 @@
import org.apache.asterix.om.typecomputer.impl.LocalAvgTypeComputer;
import org.apache.asterix.om.typecomputer.impl.MinMaxAggTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NonTaggedGetItemResultType;
-import org.apache.asterix.om.typecomputer.impl.NotMissingTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.NotUnknownTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NullableDoubleTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NumericAddSubMulDivTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NumericAggTypeComputer;
@@ -810,7 +810,7 @@
addFunction(DEEP_EQUAL, BooleanFunctionTypeComputer.INSTANCE, true);
// and then, Asterix builtin functions
- addPrivateFunction(CHECK_UNKNOWN, NotMissingTypeComputer.INSTANCE, true);
+ addPrivateFunction(CHECK_UNKNOWN, NotUnknownTypeComputer.INSTANCE, true);
addPrivateFunction(ANY_COLLECTION_MEMBER, CollectionMemberResultType.INSTANCE, true);
addFunction(BOOLEAN_CONSTRUCTOR, StringBooleanTypeComputer.INSTANCE, true);
addFunction(CARET, NumericAddSubMulDivTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FlowRecordResultTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FlowRecordResultTypeComputer.java
deleted file mode 100644
index 27e8a3d..0000000
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FlowRecordResultTypeComputer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.asterix.om.typecomputer.impl;
-
-import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
-import org.apache.asterix.om.typecomputer.base.TypeCastUtils;
-import org.apache.asterix.om.types.IAType;
-import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
-import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
-import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-
-public class FlowRecordResultTypeComputer implements IResultTypeComputer {
-
- public static final FlowRecordResultTypeComputer INSTANCE = new FlowRecordResultTypeComputer();
-
- @Override
- public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
- IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
- ScalarFunctionCallExpression funcExpr = (ScalarFunctionCallExpression) expression;
- IAType type = TypeCastUtils.getRequiredType(funcExpr);
- if (type == null) {
- type = (IAType) env.getType(funcExpr.getArguments().get(0).getValue());
- }
- return type;
- }
-}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotMissingTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotUnknownTypeComputer.java
similarity index 65%
rename from asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotMissingTypeComputer.java
rename to asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotUnknownTypeComputer.java
index 68444c4..163ff30 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotMissingTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NotUnknownTypeComputer.java
@@ -19,9 +19,6 @@
package org.apache.asterix.om.typecomputer.impl;
-import java.util.ArrayList;
-import java.util.List;
-
import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
import org.apache.asterix.om.types.ATypeTag;
import org.apache.asterix.om.types.AUnionType;
@@ -33,16 +30,13 @@
import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
/**
- * This class is the type computer for not-missing function.
+ * This class is the type computer for check-unknown function.
* If the input type is not a union, we just return it.
- * If the input type is a union,
- * case 1: we return a new union without missing if the new union still has more than one types;
- * case 2: we return the non-missing item type in the original union if there are only missing
- * and it in the original union.
+ * If the input type is a union, we return the actual, non-null/non-missing type.
*/
-public class NotMissingTypeComputer implements IResultTypeComputer {
+public class NotUnknownTypeComputer implements IResultTypeComputer {
- public static final NotMissingTypeComputer INSTANCE = new NotMissingTypeComputer();
+ public static final NotUnknownTypeComputer INSTANCE = new NotUnknownTypeComputer();
@Override
public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
@@ -53,25 +47,7 @@
// directly return the input type if it is not a union
return type;
}
-
AUnionType unionType = (AUnionType) type;
- List<IAType> items = new ArrayList<>();
- // copy the item types
- items.addAll(unionType.getUnionList());
-
- // remove missing
- for (int i = items.size() - 1; i >= 0; i--) {
- IAType itemType = items.get(i);
- if (itemType.getTypeTag() == ATypeTag.MISSING) {
- items.remove(i);
- }
- }
- if (items.size() == 1) {
- //only one type is left
- return items.get(0);
- } else {
- //more than two types are left
- return new AUnionType(items, unionType.getTypeName());
- }
+ return unionType.getActualType();
}
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NullableDoubleTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NullableDoubleTypeComputer.java
index 94c75d2..be3138d 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NullableDoubleTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NullableDoubleTypeComputer.java
@@ -37,6 +37,6 @@
@Override
public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
- return AUnionType.createMissableType(BuiltinType.ADOUBLE, "OptionalDouble");
+ return AUnionType.createNullableType(BuiltinType.ADOUBLE, "OptionalDouble");
}
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/RecordAddFieldsTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/RecordAddFieldsTypeComputer.java
index 289db7c..6d29c36 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/RecordAddFieldsTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/RecordAddFieldsTypeComputer.java
@@ -40,7 +40,6 @@
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
-import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractLogicalExpression;
import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
@@ -56,7 +55,6 @@
@Override
public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
-
AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) expression;
IAType type0 = (IAType) env.getType(funcExpr.getArguments().get(0).getValue());
@@ -65,7 +63,7 @@
throw new AlgebricksException("Input record cannot be null");
}
- AbstractLogicalExpression arg1 = (AbstractLogicalExpression) funcExpr.getArguments().get(1).getValue();
+ ILogicalExpression arg1 = funcExpr.getArguments().get(1).getValue();
IAType type1 = (IAType) env.getType(arg1);
AOrderedListType inputOrderedListType = TypeComputeUtils.extractOrderedListType(type1);
if (inputOrderedListType == null) {
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/SubsetCollectionTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/SubsetCollectionTypeComputer.java
index 40c57a4..3fcb9bb 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/SubsetCollectionTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/SubsetCollectionTypeComputer.java
@@ -22,6 +22,7 @@
import org.apache.asterix.om.types.ATypeTag;
import org.apache.asterix.om.types.AUnionType;
import org.apache.asterix.om.types.AbstractCollectionType;
+import org.apache.asterix.om.types.BuiltinType;
import org.apache.asterix.om.types.IAType;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -65,6 +66,8 @@
}
throw new AlgebricksException("Expecting collection type. Found " + t);
}
+ case ANY:
+ return BuiltinType.ANY;
default: {
throw new AlgebricksException("Expecting collection type. Found " + t);
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TypeComputeUtils.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TypeComputeUtils.java
index 711b840..823f51e 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TypeComputeUtils.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TypeComputeUtils.java
@@ -111,19 +111,27 @@
byte category = CERTAIN;
boolean meetNull = false;
for (IAType inputType : inputTypes) {
- ATypeTag inputTypeTag = inputType.getTypeTag();
- if (inputTypeTag == ATypeTag.UNION) {
- AUnionType unionType = (AUnionType) inputType;
- if (unionType.isNullableType()) {
+ switch (inputType.getTypeTag()) {
+ case UNION:
+ AUnionType unionType = (AUnionType) inputType;
+ if (unionType.isNullableType()) {
+ category |= NULLABLE;
+ }
+ if (unionType.isMissableType()) {
+ category |= MISSABLE;
+ }
+ break;
+ case MISSING:
+ return MISSING;
+ case NULL:
+ meetNull = true;
+ break;
+ case ANY:
category |= NULLABLE;
- }
- if (unionType.isMissableType()) {
category |= MISSABLE;
- }
- } else if (inputTypeTag == ATypeTag.MISSING) {
- return MISSING;
- } else if (inputTypeTag == ATypeTag.NULL) {
- meetNull = true;
+ break;
+ default:
+ break;
}
}
if (meetNull) {
@@ -133,6 +141,9 @@
}
private static IAType getResultType(IAType type, byte category) {
+ if (type.getTypeTag() == ATypeTag.ANY) {
+ return type;
+ }
if (category == CERTAIN) {
return type;
}
@@ -185,6 +196,8 @@
} else {
return null;
}
+ case ANY:
+ return ARecordType.FULLY_OPEN_RECORD_TYPE;
default:
return null;
}
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java
new file mode 100644
index 0000000..760fd18
--- /dev/null
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.asterix.om.typecomputer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
+import org.junit.Assert;
+import org.junit.Test;
+import org.reflections.Reflections;
+import org.reflections.scanners.SubTypesScanner;
+
+// Tests if all type computers can handle input type ANY properly.
+public class TypeComputerTest {
+
+ @Test
+ public void test() throws Exception {
+ // Several exceptional type computers.
+ Set<String> exceptionalTypeComputers = new HashSet<>();
+ exceptionalTypeComputers.add("InjectFailureTypeComputer");
+ exceptionalTypeComputers.add("RecordAddFieldsTypeComputer");
+ exceptionalTypeComputers.add("OpenRecordConstructorResultType");
+ exceptionalTypeComputers.add("RecordRemoveFieldsTypeComputer");
+ exceptionalTypeComputers.add("ClosedRecordConstructorResultType");
+ exceptionalTypeComputers.add("LocalAvgTypeComputer");
+ exceptionalTypeComputers.add("BooleanOnlyTypeComputer");
+ exceptionalTypeComputers.add("AMissingTypeComputer");
+ exceptionalTypeComputers.add("NullableDoubleTypeComputer");
+ exceptionalTypeComputers.add("RecordMergeTypeComputer");
+
+ // Tests all usual type computers.
+ Reflections reflections = new Reflections("org.apache.asterix.om.typecomputer", new SubTypesScanner(false));
+ Set<Class<? extends IResultTypeComputer>> classes = reflections.getSubTypesOf(IResultTypeComputer.class);
+ for (Class<? extends IResultTypeComputer> c : classes) {
+ if (exceptionalTypeComputers.contains(c.getSimpleName()) || Modifier.isAbstract(c.getModifiers())) {
+ continue;
+ }
+ System.out.println("Test type computer: " + c.getName());
+ Assert.assertTrue(testTypeComputer(c));
+ }
+ }
+
+ private boolean testTypeComputer(Class<? extends IResultTypeComputer> c) throws Exception {
+ // Mocks the type environment.
+ IVariableTypeEnvironment mockTypeEnv = mock(IVariableTypeEnvironment.class);
+ // Mocks the metadata provider.
+ IMetadataProvider<?, ?> mockMetadataProvider = mock(IMetadataProvider.class);
+
+ // Mocks function expression.
+ AbstractFunctionCallExpression mockExpr = mock(AbstractFunctionCallExpression.class);
+
+ // A function at most has six argument.
+ List<Mutable<ILogicalExpression>> argRefs = new ArrayList<>();
+ for (int argIndex = 0; argIndex < 6; ++argIndex) {
+ ILogicalExpression mockArg = mock(ILogicalExpression.class);
+ argRefs.add(new MutableObject<>(mockArg));
+ when(mockTypeEnv.getType(mockArg)).thenReturn(BuiltinType.ANY);
+ }
+
+ // Sets up arguments for the mocked expression.
+ when(mockExpr.getArguments()).thenReturn(argRefs);
+
+ // Sets up required/actual types of the mocked expression.
+ Object[] opaqueParameters = new Object[2];
+ opaqueParameters[0] = BuiltinType.ANY;
+ opaqueParameters[1] = BuiltinType.ANY;
+ when(mockExpr.getOpaqueParameters()).thenReturn(opaqueParameters);
+
+ // Tests the return type. It should be either ANY or NULLABLE/MISSABLE.
+ IResultTypeComputer instance = (IResultTypeComputer) c.getField("INSTANCE").get(null);
+ IAType resultType = instance.computeType(mockExpr, mockTypeEnv, mockMetadataProvider);
+ ATypeTag typeTag = resultType.getTypeTag();
+ if (typeTag == ATypeTag.ANY) {
+ return true;
+ }
+ if (typeTag == ATypeTag.UNION) {
+ AUnionType unionType = (AUnionType) resultType;
+ return unionType.isMissableType() && unionType.isNullableType();
+ }
+ return false;
+ }
+}