Michael Blow has submitted this change and it was merged. Change subject: Add internal function for type casting in 'lax' mode ......................................................................
Add internal function for type casting in 'lax' mode - Add internal function 'cast-lax' which performs type demotion in 'lax' mode and returns 'missing' if cast fails - Fixed incorrect boundary check when converting from double to float Change-Id: Id929f1e66853f0603d033cf0f824349296e83521 Reviewed-on: https://asterix-gerrit.ics.uci.edu/1806 Sonar-Qube: Jenkins <[email protected]> Integration-Tests: Jenkins <[email protected]> Tested-by: Jenkins <[email protected]> BAD: Jenkins <[email protected]> Reviewed-by: Yingyi Bu <[email protected]> --- M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java A asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/CastTypeLaxTest.java M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/cast/ACastVisitor.java A asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CastTypeLaxComputer.java M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToFloatTypeConvertComputer.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeDescriptor.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxDescriptor.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxEvaluator.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java 11 files changed, 564 insertions(+), 76 deletions(-) Approvals: Yingyi Bu: Looks good to me, approved Jenkins: Verified; No violations found; No violations found; Verified diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java index 7431218..53efe2f 100644 --- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java +++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java @@ -161,6 +161,7 @@ import org.apache.asterix.runtime.evaluators.functions.IfMissingDescriptor; import org.apache.asterix.runtime.evaluators.functions.IfMissingOrNullDescriptor; import org.apache.asterix.runtime.evaluators.functions.IfNullDescriptor; +import org.apache.asterix.runtime.evaluators.functions.CastTypeLaxDescriptor; import org.apache.asterix.runtime.evaluators.functions.InjectFailureDescriptor; import org.apache.asterix.runtime.evaluators.functions.IsArrayDescriptor; import org.apache.asterix.runtime.evaluators.functions.IsBooleanDescriptor; @@ -697,6 +698,7 @@ // Cast function functionsToInjectUnkownHandling.add(CastTypeDescriptor.FACTORY); + functionsToInjectUnkownHandling.add(CastTypeLaxDescriptor.FACTORY); // Record function functionsToInjectUnkownHandling.add(RecordPairsDescriptor.FACTORY); diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/CastTypeLaxTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/CastTypeLaxTest.java new file mode 100644 index 0000000..e3117b3 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/CastTypeLaxTest.java @@ -0,0 +1,250 @@ +/* + * 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.runtime; + +import static org.mockito.Mockito.mock; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.asterix.dataflow.data.nontagged.serde.AObjectSerializerDeserializer; +import org.apache.asterix.om.base.ABoolean; +import org.apache.asterix.om.base.ADouble; +import org.apache.asterix.om.base.AFloat; +import org.apache.asterix.om.base.AInt16; +import org.apache.asterix.om.base.AInt32; +import org.apache.asterix.om.base.AInt64; +import org.apache.asterix.om.base.AInt8; +import org.apache.asterix.om.base.AMissing; +import org.apache.asterix.om.base.IAObject; +import org.apache.asterix.om.functions.IFunctionDescriptor; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.om.types.IAType; +import org.apache.asterix.om.types.hierachy.ATypeHierarchy; +import org.apache.asterix.runtime.evaluators.functions.CastTypeLaxDescriptor; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.algebricks.runtime.evaluators.ConstantEvalFactory; +import org.apache.hyracks.api.context.IHyracksTaskContext; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(value = Parameterized.class) +public class CastTypeLaxTest { + + private IAType inType; + + private IAObject inValue; + + private IAObject targetType; + + private IAObject targetValue; + + public CastTypeLaxTest(IAType inType, IAObject inValue, IAObject targetType, IAObject targetValue) { + this.inType = inType; + this.inValue = inValue; + this.targetType = targetType; + this.targetValue = targetValue; + } + + @Test + public void testCastLax() throws Exception { + IFunctionDescriptor funcDesc = CastTypeLaxDescriptor.FACTORY.createFunctionDescriptor(); + + funcDesc.setImmutableStates(targetType, inType); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + AObjectSerializerDeserializer serDe = AObjectSerializerDeserializer.INSTANCE; + serDe.serialize(inValue, new DataOutputStream(baos)); + + ConstantEvalFactory argEvalFactory = new ConstantEvalFactory(baos.toByteArray()); + IScalarEvaluatorFactory evalFactory = + funcDesc.createEvaluatorFactory(new IScalarEvaluatorFactory[] { argEvalFactory }); + IHyracksTaskContext ctx = mock(IHyracksTaskContext.class); + IScalarEvaluator evaluator = evalFactory.createScalarEvaluator(ctx); + VoidPointable resultPointable = new VoidPointable(); + evaluator.evaluate(null, resultPointable); + + ByteArrayInputStream bais = new ByteArrayInputStream(resultPointable.getByteArray(), + resultPointable.getStartOffset(), resultPointable.getLength()); + IAObject resultValue = serDe.deserialize(new DataInputStream(bais)); + + Assert.assertTrue(String.format("Expected: %s, actual: %s", targetValue, resultValue), + targetValue.deepEqual(resultValue)); + } + + @Parameterized.Parameters(name = "CastTypeLaxTest {index}: {0}({1})->{2}({3})") + public static Collection<Object[]> tests() throws Exception { + List<Object[]> tests = new ArrayList<>(); + + IAType[] numericTypes = new IAType[] { BuiltinType.AINT8, BuiltinType.AINT16, BuiltinType.AINT32, + BuiltinType.AINT64, BuiltinType.AFLOAT, BuiltinType.ADOUBLE }; + + // numeric -> numeric + for (IAType inType : numericTypes) { + for (IAType targetType : numericTypes) { + if (!inType.equals(targetType)) { + generateTests(tests, inType, targetType); + } + } + } + + // type mismatch -> missing + addTest(tests, BuiltinType.ABOOLEAN, ABoolean.TRUE, BuiltinType.ADATE, AMissing.MISSING); + + return tests; + } + + private static void generateTests(List<Object[]> outTests, IAType inType, IAType targetType) { + int value = inType.getTypeTag().ordinal(); + addTest(outTests, inType, createValue(inType, value), targetType, createValue(targetType, value)); + + if (ATypeHierarchy.canDemote(inType.getTypeTag(), targetType.getTypeTag())) { + IAObject inMax = createValue(inType, getMax(inType)); + IAObject inMin = createValue(inType, getMin(inType)); + IAObject targetMax = createValue(targetType, getMax(targetType)); + IAObject targetMin = createValue(targetType, getMin(targetType)); + + addTest(outTests, inType, inMax, targetType, targetMax); + addTest(outTests, inType, inMin, targetType, targetMin); + + if (!isInteger(inType) && isInteger(targetType)) { + addTest(outTests, inType, createValue(inType, getPositiveInfinity(inType)), targetType, targetMax); + addTest(outTests, inType, createValue(inType, getNegativeInfinity(inType)), targetType, targetMin); + addTest(outTests, inType, createValue(inType, getNaN(inType)), targetType, createValue(targetType, 0)); + } + } + } + + private static void addTest(List<Object[]> outTests, IAType inType, IAObject inValue, IAObject targetType, + IAObject targetValue) { + outTests.add(new Object[] { inType, inValue, targetType, targetValue }); + } + + private static IAObject createValue(IAType type, Number value) { + switch (type.getTypeTag()) { + case TINYINT: + return new AInt8(value.byteValue()); + case SMALLINT: + return new AInt16(value.shortValue()); + case INTEGER: + return new AInt32(value.intValue()); + case BIGINT: + return new AInt64(value.longValue()); + case FLOAT: + return new AFloat(value.floatValue()); + case DOUBLE: + return new ADouble(value.doubleValue()); + default: + throw new IllegalStateException(type.toString()); + } + } + + private static Number getMax(IAType type) { + switch (type.getTypeTag()) { + case TINYINT: + return Byte.MAX_VALUE; + case SMALLINT: + return Short.MAX_VALUE; + case INTEGER: + return Integer.MAX_VALUE; + case BIGINT: + return Long.MAX_VALUE; + case FLOAT: + return Float.MAX_VALUE; + case DOUBLE: + return Double.MAX_VALUE; + default: + throw new IllegalStateException(type.toString()); + } + } + + private static Number getMin(IAType type) { + switch (type.getTypeTag()) { + case TINYINT: + return Byte.MIN_VALUE; + case SMALLINT: + return Short.MIN_VALUE; + case INTEGER: + return Integer.MIN_VALUE; + case BIGINT: + return Long.MIN_VALUE; + case FLOAT: + return -Float.MAX_VALUE; + case DOUBLE: + return -Double.MAX_VALUE; + default: + throw new IllegalStateException(type.toString()); + } + } + + private static Number getPositiveInfinity(IAType type) { + switch (type.getTypeTag()) { + case FLOAT: + return Float.POSITIVE_INFINITY; + case DOUBLE: + return Double.POSITIVE_INFINITY; + default: + throw new IllegalStateException(type.toString()); + } + } + + private static Number getNegativeInfinity(IAType type) { + switch (type.getTypeTag()) { + case FLOAT: + return Float.NEGATIVE_INFINITY; + case DOUBLE: + return Double.NEGATIVE_INFINITY; + default: + throw new IllegalStateException(type.toString()); + } + } + + private static Number getNaN(IAType type) { + switch (type.getTypeTag()) { + case FLOAT: + return Float.NaN; + case DOUBLE: + return Double.NaN; + default: + throw new IllegalStateException(type.toString()); + } + } + + private static boolean isInteger(IAType type) { + switch (type.getTypeTag()) { + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + return true; + default: + return false; + } + } +} diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java index 6fc3ed9..f4fb36a 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java @@ -57,6 +57,7 @@ import org.apache.asterix.om.typecomputer.impl.BooleanOnlyTypeComputer; import org.apache.asterix.om.typecomputer.impl.BooleanOrMissingTypeComputer; import org.apache.asterix.om.typecomputer.impl.CastTypeComputer; +import org.apache.asterix.om.typecomputer.impl.CastTypeLaxComputer; import org.apache.asterix.om.typecomputer.impl.ClosedRecordConstructorResultType; import org.apache.asterix.om.typecomputer.impl.CollectionMemberResultType; import org.apache.asterix.om.typecomputer.impl.CollectionToSequenceTypeComputer; @@ -663,6 +664,8 @@ "flow-object", 1); public static final FunctionIdentifier CAST_TYPE = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "cast", 1); + public static final FunctionIdentifier CAST_TYPE_LAX = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, + "cast-lax", 1); public static final FunctionIdentifier CREATE_UUID = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "create-uuid", 0); @@ -1077,6 +1080,7 @@ addFunction(SLEEP, SleepTypeComputer.INSTANCE, false); addPrivateFunction(INJECT_FAILURE, InjectFailureTypeComputer.INSTANCE, true); addPrivateFunction(CAST_TYPE, CastTypeComputer.INSTANCE, true); + addPrivateFunction(CAST_TYPE_LAX, CastTypeLaxComputer.INSTANCE, true); addFunction(TID, AInt64TypeComputer.INSTANCE, true); addFunction(TIME_CONSTRUCTOR, ATimeTypeComputer.INSTANCE, true); diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/cast/ACastVisitor.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/cast/ACastVisitor.java index b161365..72e3072 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/cast/ACastVisitor.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/cast/ACastVisitor.java @@ -56,6 +56,16 @@ private final Map<IVisitablePointable, AListCaster> laccessorToCaster = new HashMap<>(); private final ArrayBackedValueStorage castBuffer = new ArrayBackedValueStorage(); + private final boolean strictDemote; + + public ACastVisitor() { + this(true); + } + + public ACastVisitor(boolean strictDemote) { + this.strictDemote = strictDemote; + } + @Override public Void visit(AListVisitablePointable accessor, Triple<IVisitablePointable, IAType, Boolean> arg) throws HyracksDataException { @@ -110,7 +120,7 @@ try { castBuffer.reset(); ATypeHierarchy.convertNumericTypeByteArray(accessor.getByteArray(), accessor.getStartOffset(), - accessor.getLength(), reqTypeTag, castBuffer.getDataOutput(), true); + accessor.getLength(), reqTypeTag, castBuffer.getDataOutput(), strictDemote); arg.first.set(castBuffer); } catch (IOException e1) { throw new HyracksDataException( diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CastTypeLaxComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CastTypeLaxComputer.java new file mode 100644 index 0000000..7aab27f --- /dev/null +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CastTypeLaxComputer.java @@ -0,0 +1,38 @@ +/* + * 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.AbstractResultTypeComputer; +import org.apache.asterix.om.typecomputer.base.IResultTypeComputer; +import org.apache.asterix.om.typecomputer.base.TypeCastUtils; +import org.apache.asterix.om.types.AUnionType; +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.AbstractFunctionCallExpression; + +public class CastTypeLaxComputer extends AbstractResultTypeComputer { + public static final IResultTypeComputer INSTANCE = new CastTypeLaxComputer(); + + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { + return AUnionType.createMissableType(TypeCastUtils.getRequiredType((AbstractFunctionCallExpression) expr)); + } +} \ No newline at end of file diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToFloatTypeConvertComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToFloatTypeConvertComputer.java index 1792ce5..70aa283 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToFloatTypeConvertComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToFloatTypeConvertComputer.java @@ -69,11 +69,11 @@ } else { return Float.MAX_VALUE; } - } else if (sourceValue < Float.MIN_VALUE) { + } else if (sourceValue < -Float.MAX_VALUE) { if (strict) { raiseBoundaryCheckException(sourceValue); } else { - return Float.MIN_VALUE; + return -Float.MAX_VALUE; } } @@ -82,6 +82,6 @@ private void raiseBoundaryCheckException(double sourceValue) throws HyracksDataException { throw new RuntimeDataException(ErrorCode.TYPE_CONVERT_OUT_OF_BOUND, sourceValue, ATypeTag.FLOAT, - Float.MAX_VALUE, Float.MIN_VALUE); + Float.MAX_VALUE, -Float.MAX_VALUE); } } diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeDescriptor.java index 22f1e5b..7f5c58d 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeDescriptor.java @@ -18,28 +18,17 @@ */ package org.apache.asterix.runtime.evaluators.functions; -import org.apache.asterix.common.exceptions.AsterixException; import org.apache.asterix.om.functions.BuiltinFunctions; import org.apache.asterix.om.functions.IFunctionDescriptor; import org.apache.asterix.om.functions.IFunctionDescriptorFactory; -import org.apache.asterix.om.pointables.PointableAllocator; -import org.apache.asterix.om.pointables.base.DefaultOpenFieldType; -import org.apache.asterix.om.pointables.base.IVisitablePointable; -import org.apache.asterix.om.pointables.cast.ACastVisitor; import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils; -import org.apache.asterix.om.types.ATypeTag; -import org.apache.asterix.om.types.BuiltinType; import org.apache.asterix.om.types.IAType; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; -import org.apache.hyracks.algebricks.common.utils.Triple; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; import org.apache.hyracks.api.context.IHyracksTaskContext; import org.apache.hyracks.api.exceptions.HyracksDataException; -import org.apache.hyracks.data.std.api.IPointable; -import org.apache.hyracks.data.std.primitive.VoidPointable; -import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; /** * This runtime function casts an input ADM instance of a certain type into the form @@ -96,64 +85,3 @@ } } -class CastTypeEvaluator implements IScalarEvaluator { - - private final IScalarEvaluator argEvaluator; - private final IPointable argPointable = new VoidPointable(); - - private final PointableAllocator allocator = new PointableAllocator(); - private final IVisitablePointable inputPointable; - private final IVisitablePointable resultPointable; - - private final ACastVisitor castVisitor = new ACastVisitor(); - private final Triple<IVisitablePointable, IAType, Boolean> arg; - - public CastTypeEvaluator(IAType reqType, IAType inputType, IScalarEvaluator argEvaluator) - throws HyracksDataException { - try { - this.argEvaluator = argEvaluator; - this.inputPointable = allocatePointable(inputType, reqType); - this.resultPointable = allocatePointable(reqType, inputType); - this.arg = new Triple<>(resultPointable, reqType, Boolean.FALSE); - } catch (AsterixException e) { - throw new HyracksDataException(e); - } - } - - @Override - public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { - try { - argEvaluator.evaluate(tuple, argPointable); - inputPointable.set(argPointable); - inputPointable.accept(castVisitor, arg); - result.set(resultPointable); - } catch (Exception ioe) { - throw new HyracksDataException(ioe); - } - } - - // Allocates the result pointable. - private final IVisitablePointable allocatePointable(IAType typeForPointable, IAType typeForOtherSide) - throws AsterixException { - if (!typeForPointable.equals(BuiltinType.ANY)) { - return allocator.allocateFieldValue(typeForPointable); - } - return allocatePointableForAny(typeForOtherSide); - } - - // Allocates an input or result pointable if the input or required type is ANY. - private IVisitablePointable allocatePointableForAny(IAType type) { - ATypeTag tag = type.getTypeTag(); - switch (tag) { - case OBJECT: - return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE); - case ARRAY: - return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_AORDERED_LIST_TYPE); - case MULTISET: - return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_AUNORDERED_LIST_TYPE); - default: - return allocator.allocateFieldValue(null); - } - } - -} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java new file mode 100644 index 0000000..524b2ed --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java @@ -0,0 +1,94 @@ +/* + * 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.runtime.evaluators.functions; + +import org.apache.asterix.om.pointables.PointableAllocator; +import org.apache.asterix.om.pointables.base.DefaultOpenFieldType; +import org.apache.asterix.om.pointables.base.IVisitablePointable; +import org.apache.asterix.om.pointables.cast.ACastVisitor; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.om.types.IAType; +import org.apache.hyracks.algebricks.common.utils.Triple; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +class CastTypeEvaluator implements IScalarEvaluator { + + private final IScalarEvaluator argEvaluator; + private final IPointable argPointable = new VoidPointable(); + + private final PointableAllocator allocator = new PointableAllocator(); + private final IVisitablePointable inputPointable; + private final IVisitablePointable resultPointable; + + private final ACastVisitor castVisitor; + private final Triple<IVisitablePointable, IAType, Boolean> arg; + + public CastTypeEvaluator(IAType reqType, IAType inputType, IScalarEvaluator argEvaluator) { + this.argEvaluator = argEvaluator; + this.inputPointable = allocatePointable(inputType, reqType); + this.resultPointable = allocatePointable(reqType, inputType); + this.arg = new Triple<>(resultPointable, reqType, Boolean.FALSE); + this.castVisitor = createCastVisitor(); + } + + protected ACastVisitor createCastVisitor() { + return new ACastVisitor(); + } + + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + argEvaluator.evaluate(tuple, argPointable); + inputPointable.set(argPointable); + cast(result); + } + + protected void cast(IPointable result) throws HyracksDataException { + inputPointable.accept(castVisitor, arg); + result.set(resultPointable); + } + + // Allocates the result pointable. + private final IVisitablePointable allocatePointable(IAType typeForPointable, IAType typeForOtherSide) { + if (!typeForPointable.equals(BuiltinType.ANY)) { + return allocator.allocateFieldValue(typeForPointable); + } + return allocatePointableForAny(typeForOtherSide); + } + + // Allocates an input or result pointable if the input or required type is ANY. + private IVisitablePointable allocatePointableForAny(IAType type) { + ATypeTag tag = type.getTypeTag(); + switch (tag) { + case OBJECT: + return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE); + case ARRAY: + return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_AORDERED_LIST_TYPE); + case MULTISET: + return allocator.allocateFieldValue(DefaultOpenFieldType.NESTED_OPEN_AUNORDERED_LIST_TYPE); + default: + return allocator.allocateFieldValue(null); + } + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxDescriptor.java new file mode 100644 index 0000000..eea1b6e --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxDescriptor.java @@ -0,0 +1,94 @@ +/* + * 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.runtime.evaluators.functions; + +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.asterix.om.functions.IFunctionDescriptor; +import org.apache.asterix.om.functions.IFunctionDescriptorFactory; +import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils; +import org.apache.asterix.om.types.IAType; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.context.IHyracksTaskContext; +import org.apache.hyracks.api.exceptions.HyracksDataException; + +/** + * Implements 'lax' cast. It differs from the regular cast as follows: + * <ul> + * <li>Numeric type demotion does not fail if the input value is not within bounds for the target type. + * Instead it returns min/max value of the target type. + * </li> + * <li> + * If there's an error during casting then 'MISSING' is returned. + * Note that errors from argument evaluation are still propagated. + * </li> + * </ul> + */ +public class CastTypeLaxDescriptor extends AbstractScalarFunctionDynamicDescriptor { + + public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { + @Override + public IFunctionDescriptor createFunctionDescriptor() { + return new CastTypeLaxDescriptor(); + } + }; + + private static final long serialVersionUID = 1L; + private IAType reqType; + private IAType inputType; + + private CastTypeLaxDescriptor() { + } + + @Override + public void setImmutableStates(Object... states) { + reqType = (IAType) states[0]; + inputType = (IAType) states[1]; + // If reqType or inputType is null, or they are the same, it indicates there is a bug in the compiler. + if (reqType == null || inputType == null || reqType.equals(inputType)) { + throw new IllegalStateException( + "Invalid types for casting, required type " + reqType + ", input type " + inputType); + } + // NULLs and MISSINGs are handled by the generated code, therefore we only need to handle actual types here. + this.reqType = TypeComputeUtils.getActualType(reqType); + this.inputType = TypeComputeUtils.getActualType(inputType); + } + + @Override + public FunctionIdentifier getIdentifier() { + return BuiltinFunctions.CAST_TYPE_LAX; + } + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { + final IScalarEvaluatorFactory recordEvalFactory = args[0]; + + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IHyracksTaskContext ctx) throws HyracksDataException { + return new CastTypeLaxEvaluator(reqType, inputType, recordEvalFactory.createScalarEvaluator(ctx)); + } + }; + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxEvaluator.java new file mode 100644 index 0000000..cbe04e2 --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeLaxEvaluator.java @@ -0,0 +1,58 @@ +/* + * 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.runtime.evaluators.functions; + +import org.apache.asterix.om.pointables.cast.ACastVisitor; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.IAType; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; + +import java.util.logging.Level; +import java.util.logging.Logger; + +class CastTypeLaxEvaluator extends CastTypeEvaluator { + + private static final Logger LOGGER = Logger.getLogger(CastTypeLaxEvaluator.class.getName()); + + private static final byte[] MISSING_BYTES = new byte[] { ATypeTag.SERIALIZED_MISSING_TYPE_TAG }; + + CastTypeLaxEvaluator(IAType reqType, IAType inputType, IScalarEvaluator argEvaluator) { + super(reqType, inputType, argEvaluator); + } + + @Override + protected ACastVisitor createCastVisitor() { + return new ACastVisitor(false); + } + + @Override + protected void cast(IPointable result) { + try { + super.cast(result); + } catch (HyracksDataException e) { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, e.toString(), e); + } + result.set(MISSING_BYTES, 0, MISSING_BYTES.length); + } + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java index b0e1788..7f4b9c4 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java @@ -440,6 +440,16 @@ fd.setImmutableStates(rt, it); } }); + functionTypeInferers.put(BuiltinFunctions.CAST_TYPE_LAX, new FunctionTypeInferer() { + @Override + public void infer(ILogicalExpression expr, IFunctionDescriptor fd, IVariableTypeEnvironment context) + throws AlgebricksException { + AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) expr; + IAType rt = TypeCastUtils.getRequiredType(funcExpr); + IAType it = (IAType) context.getType(funcExpr.getArguments().get(0).getValue()); + fd.setImmutableStates(rt, it); + } + }); functionTypeInferers.put(BuiltinFunctions.OPEN_RECORD_CONSTRUCTOR, new FunctionTypeInferer() { @Override public void infer(ILogicalExpression expr, IFunctionDescriptor fd, IVariableTypeEnvironment context) -- To view, visit https://asterix-gerrit.ics.uci.edu/1806 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: merged Gerrit-Change-Id: Id929f1e66853f0603d033cf0f824349296e83521 Gerrit-PatchSet: 3 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Dmitry Lychagin <[email protected]> Gerrit-Reviewer: Jenkins <[email protected]> Gerrit-Reviewer: Michael Blow <[email protected]> Gerrit-Reviewer: Till Westmann <[email protected]> Gerrit-Reviewer: Yingyi Bu <[email protected]>
