Skip to content
Permalink
Browse files
[NO ISSUE][FUN] Make default-null functions type computers always nul…
…lable

- user model changes: no
- storage format changes: no
- interface changes: no

Change-Id: I8060c98e992d6c8e8dbb15173e35b3c3b58a2a3a
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/14084
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
  • Loading branch information
AliSolaiman committed Nov 17, 2021
1 parent 0129fdd commit ded5f56d3b00356354dbf65dea7df79248c0eb75
Show file tree
Hide file tree
Showing 29 changed files with 207 additions and 68 deletions.
@@ -1046,15 +1046,16 @@ public boolean acceptsFunction(AbstractFunctionCallExpression functionExpr, IATy
if (!finalStep) {
return AccessMethodUtils.isFieldAccess(funId);
}
if (AccessMethodUtils.isFieldAccess(funId)) {
return !defaultNull;
} else if (defaultNull && CAST_NULL_TYPE_CONSTRUCTORS.contains(funId)) {
if (defaultNull) {
if (!CAST_NULL_TYPE_CONSTRUCTORS.contains(funId)) {
return false;
}
IAType nonNullableType = Index.getNonNullableType(indexedFieldType).first;
FunctionIdentifier indexedFieldConstructor = TypeUtil.getTypeConstructorDefaultNull(nonNullableType);
// index should have CAST (DEFAULT NULL) and the applied function should be the same as the indexed field
// index has CAST (DEFAULT NULL); the applied function should be the same as the indexed field function
return funId.equals(indexedFieldConstructor);
} else {
return false;
return AccessMethodUtils.isFieldAccess(funId);
}
}

@@ -78,8 +78,7 @@ public static Collection<Object[]> tests() {
// We test all functions except record and cast functions, which requires type settings (we test them
// in runtime tests).
// TODO(ali): ASTERIXDB-2982 do it in a proper way so that it does not exclude classes inadvertently
if (!className.contains("record") && !className.contains("Cast")
&& !className.contains("DefaultNull")) {
if (!className.contains("record") && !className.contains("Cast")) {
tests.add(new Object[] { getTestName(functionDescriptor.getClass()), functionDescriptor });
} else {
LOGGER.log(Level.INFO, "Excluding " + className);
@@ -20,6 +20,10 @@

package org.apache.asterix.test.runtime;

import static org.apache.asterix.common.annotations.MissingNullInOutFunction.MissingNullType.MISSING;
import static org.apache.asterix.common.annotations.MissingNullInOutFunction.MissingNullType.NULL;
import static org.apache.asterix.om.types.ATypeTag.SERIALIZED_MISSING_TYPE_TAG;
import static org.apache.asterix.om.types.ATypeTag.SERIALIZED_NULL_TYPE_TAG;
import static org.mockito.Mockito.mock;

import java.util.ArrayList;
@@ -33,7 +37,6 @@
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.types.ATypeTag;
import org.apache.asterix.om.types.BuiltinType;
import org.apache.asterix.om.types.IAType;
import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
@@ -91,7 +94,7 @@ public static Collection<Object[]> tests() {
// Instead, we test them in runtime tests.
// TODO(ali): ASTERIXDB-2982 do it in a proper way so that it does not exclude classes inadvertently
if (!className.contains("record") && !className.contains("Cast")
&& !className.contains("FullTextContains") && !className.contains("DefaultNull")) {
&& !className.contains("FullTextContains")) {
tests.add(new Object[] { getTestName(functionDescriptor.getClass()), functionDescriptor });
} else {
LOGGER.log(Level.INFO, "Excluding " + className);
@@ -129,6 +132,9 @@ public void test() throws Exception {
int inputArity = funcDesc.getIdentifier().getArity();
Iterator<Pair<IScalarEvaluatorFactory[], IAType[]>> argEvalFactoryIterator = getArgCombinations(inputArity);
int index = 0;
MissingNullInOutFunction annot = functionDescriptor.getClass().getAnnotation(MissingNullInOutFunction.class);
byte missingOut = annot.onMissing() == MISSING ? SERIALIZED_MISSING_TYPE_TAG : SERIALIZED_NULL_TYPE_TAG;
byte nullOut = annot.onNull() == NULL ? SERIALIZED_NULL_TYPE_TAG : SERIALIZED_MISSING_TYPE_TAG;

// Test is happening here
while (argEvalFactoryIterator.hasNext()) {
@@ -148,11 +154,9 @@ public void test() throws Exception {

// Result checks
if (index != 0) {
Assert.assertEquals(ATypeTag.SERIALIZED_MISSING_TYPE_TAG,
resultPointable.getByteArray()[resultPointable.getStartOffset()]);
Assert.assertEquals(missingOut, resultPointable.getByteArray()[resultPointable.getStartOffset()]);
} else {
Assert.assertEquals(ATypeTag.SERIALIZED_NULL_TYPE_TAG,
resultPointable.getByteArray()[resultPointable.getStartOffset()]);
Assert.assertEquals(nullOut, resultPointable.getByteArray()[resultPointable.getStartOffset()]);
}
++index;
}
@@ -179,11 +183,10 @@ public Pair<IScalarEvaluatorFactory[], IAType[]> next() {
if ((index & (1 << j)) != 0) {
argumentTypes[j] = BuiltinType.AMISSING;
scalarEvaluatorFactories[j] =
new ConstantEvalFactory(new byte[] { ATypeTag.SERIALIZED_MISSING_TYPE_TAG });
new ConstantEvalFactory(new byte[] { SERIALIZED_MISSING_TYPE_TAG });
} else {
argumentTypes[j] = BuiltinType.ANULL;
scalarEvaluatorFactories[j] =
new ConstantEvalFactory(new byte[] { ATypeTag.SERIALIZED_NULL_TYPE_TAG });
scalarEvaluatorFactories[j] = new ConstantEvalFactory(new byte[] { SERIALIZED_NULL_TYPE_TAG });
}
}
++index;
@@ -25,10 +25,20 @@
import java.lang.annotation.Target;

/**
* The functions to which this annotation is applied, respect the missing/null in -> missing/null out behaviour
* The functions to which this annotation is applied, specify what {@link MissingNullType} to return on MISSING or NULL
* input, and their runtimes respect the specification.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MissingNullInOutFunction {

enum MissingNullType {
MISSING,
NULL
}

MissingNullType onMissing() default MissingNullType.MISSING;

MissingNullType onNull() default MissingNullType.NULL;
}
@@ -102,6 +102,7 @@
import org.apache.asterix.om.typecomputer.impl.NotUnknownTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NullIfTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NullableDoubleTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NullableTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NumericAddSubMulDivTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NumericBinaryToDoubleTypeComputer;
import org.apache.asterix.om.typecomputer.impl.NumericDivideTypeComputer;
@@ -1794,28 +1795,28 @@ public class BuiltinFunctions {
addPrivateFunction(MAKE_FIELD_NAME_HANDLE, AnyTypeComputer.INSTANCE, true);

// cast null type constructors
addPrivateFunction(BOOLEAN_DEFAULT_NULL_CONSTRUCTOR, ABooleanTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(INT8_DEFAULT_NULL_CONSTRUCTOR, AInt8TypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(INT16_DEFAULT_NULL_CONSTRUCTOR, AInt16TypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(INT32_DEFAULT_NULL_CONSTRUCTOR, AInt32TypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(INT64_DEFAULT_NULL_CONSTRUCTOR, AInt64TypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(FLOAT_DEFAULT_NULL_CONSTRUCTOR, AFloatTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DOUBLE_DEFAULT_NULL_CONSTRUCTOR, ADoubleTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(STRING_DEFAULT_NULL_CONSTRUCTOR, AStringTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DATE_DEFAULT_NULL_CONSTRUCTOR, ADateTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DATE_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, ADateTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(TIME_DEFAULT_NULL_CONSTRUCTOR, ATimeTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(TIME_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, ATimeTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DATETIME_DEFAULT_NULL_CONSTRUCTOR, ADateTimeTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DATETIME_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, ADateTimeTypeComputer.INSTANCE_NULLABLE,
addPrivateFunction(BOOLEAN_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_BOOLEAN, true);
addPrivateFunction(INT8_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_INT8, true);
addPrivateFunction(INT16_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_INT16, true);
addPrivateFunction(INT32_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_INT32, true);
addPrivateFunction(INT64_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_INT64, true);
addPrivateFunction(FLOAT_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_FLOAT, true);
addPrivateFunction(DOUBLE_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_DOUBLE, true);
addPrivateFunction(STRING_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_STRING, true);
addPrivateFunction(DATE_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_DATE, true);
addPrivateFunction(DATE_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, NullableTypeComputer.INSTANCE_DATE, true);
addPrivateFunction(TIME_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_TIME, true);
addPrivateFunction(TIME_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, NullableTypeComputer.INSTANCE_TIME, true);
addPrivateFunction(DATETIME_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_DATE_TIME, true);
addPrivateFunction(DATETIME_DEFAULT_NULL_CONSTRUCTOR_WITH_FORMAT, NullableTypeComputer.INSTANCE_DATE_TIME,
true);
addPrivateFunction(DURATION_DEFAULT_NULL_CONSTRUCTOR, ADurationTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(DAY_TIME_DURATION_DEFAULT_NULL_CONSTRUCTOR, ADayTimeDurationTypeComputer.INSTANCE_NULLABLE,
addPrivateFunction(DURATION_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_DURATION, true);
addPrivateFunction(DAY_TIME_DURATION_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_DAY_TIME_DURATION,
true);
addPrivateFunction(YEAR_MONTH_DURATION_DEFAULT_NULL_CONSTRUCTOR,
AYearMonthDurationTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(UUID_DEFAULT_NULL_CONSTRUCTOR, AUUIDTypeComputer.INSTANCE_NULLABLE, true);
addPrivateFunction(BINARY_BASE64_DEFAULT_NULL_CONSTRUCTOR, ABinaryTypeComputer.INSTANCE_NULLABLE, true);
NullableTypeComputer.INSTANCE_YEAR_MONTH_DURATION, true);
addPrivateFunction(UUID_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_UUID, true);
addPrivateFunction(BINARY_BASE64_DEFAULT_NULL_CONSTRUCTOR, NullableTypeComputer.INSTANCE_BINARY, true);

addPrivateFunction(NUMERIC_UNARY_MINUS, NumericUnaryTypeComputer.INSTANCE, true);
addPrivateFunction(NUMERIC_SUBTRACT, NumericAddSubMulDivTypeComputer.INSTANCE_SUB, true);
@@ -0,0 +1,77 @@
/*
* 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 static org.apache.asterix.om.types.BuiltinType.ABINARY;
import static org.apache.asterix.om.types.BuiltinType.ABOOLEAN;
import static org.apache.asterix.om.types.BuiltinType.ADATE;
import static org.apache.asterix.om.types.BuiltinType.ADATETIME;
import static org.apache.asterix.om.types.BuiltinType.ADAYTIMEDURATION;
import static org.apache.asterix.om.types.BuiltinType.ADOUBLE;
import static org.apache.asterix.om.types.BuiltinType.ADURATION;
import static org.apache.asterix.om.types.BuiltinType.AFLOAT;
import static org.apache.asterix.om.types.BuiltinType.AINT16;
import static org.apache.asterix.om.types.BuiltinType.AINT32;
import static org.apache.asterix.om.types.BuiltinType.AINT64;
import static org.apache.asterix.om.types.BuiltinType.AINT8;
import static org.apache.asterix.om.types.BuiltinType.ASTRING;
import static org.apache.asterix.om.types.BuiltinType.ATIME;
import static org.apache.asterix.om.types.BuiltinType.AUUID;
import static org.apache.asterix.om.types.BuiltinType.AYEARMONTHDURATION;

import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
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.IVariableTypeEnvironment;
import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;

public class NullableTypeComputer implements IResultTypeComputer {

public static final NullableTypeComputer INSTANCE_INT8 = new NullableTypeComputer(AINT8);
public static final NullableTypeComputer INSTANCE_INT16 = new NullableTypeComputer(AINT16);
public static final NullableTypeComputer INSTANCE_INT32 = new NullableTypeComputer(AINT32);
public static final NullableTypeComputer INSTANCE_INT64 = new NullableTypeComputer(AINT64);
public static final NullableTypeComputer INSTANCE_FLOAT = new NullableTypeComputer(AFLOAT);
public static final NullableTypeComputer INSTANCE_DOUBLE = new NullableTypeComputer(ADOUBLE);
public static final NullableTypeComputer INSTANCE_BOOLEAN = new NullableTypeComputer(ABOOLEAN);
public static final NullableTypeComputer INSTANCE_STRING = new NullableTypeComputer(ASTRING);
public static final NullableTypeComputer INSTANCE_DATE = new NullableTypeComputer(ADATE);
public static final NullableTypeComputer INSTANCE_TIME = new NullableTypeComputer(ATIME);
public static final NullableTypeComputer INSTANCE_DATE_TIME = new NullableTypeComputer(ADATETIME);
public static final NullableTypeComputer INSTANCE_DURATION = new NullableTypeComputer(ADURATION);
public static final NullableTypeComputer INSTANCE_DAY_TIME_DURATION = new NullableTypeComputer(ADAYTIMEDURATION);
public static final NullableTypeComputer INSTANCE_YEAR_MONTH_DURATION =
new NullableTypeComputer(AYEARMONTHDURATION);
public static final NullableTypeComputer INSTANCE_UUID = new NullableTypeComputer(AUUID);
public static final NullableTypeComputer INSTANCE_BINARY = new NullableTypeComputer(ABINARY);

private final IAType nullablePrimeType;

private NullableTypeComputer(IAType primeType) {
this.nullablePrimeType = AUnionType.createNullableType(primeType);
}

@Override
public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
return nullablePrimeType;
}
}
@@ -30,14 +30,16 @@
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;

@MissingNullInOutFunction
@MissingNullInOutFunction(onMissing = MissingNullInOutFunction.MissingNullType.NULL)
public class ABinaryBase64StringDefaultNullConstructorDescriptor extends AbstractScalarFunctionDynamicDescriptor {

private static final long serialVersionUID = 1L;
public static final IFunctionDescriptorFactory FACTORY = ABinaryBase64StringDefaultNullConstructorDescriptor::new;

@Override
public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
return new IScalarEvaluatorFactory() {

private static final long serialVersionUID = 1L;

@Override
@@ -30,19 +30,22 @@
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;

@MissingNullInOutFunction
@MissingNullInOutFunction(onMissing = MissingNullInOutFunction.MissingNullType.NULL)
public class ABooleanDefaultNullConstructorDescriptor extends AbstractScalarFunctionDynamicDescriptor {

private static final long serialVersionUID = 1L;
public static final IFunctionDescriptorFactory FACTORY = ABooleanDefaultNullConstructorDescriptor::new;

@Override
public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
return new IScalarEvaluatorFactory() {

private static final long serialVersionUID = 1L;

@Override
public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException {
return new AbstractBooleanConstructorEvaluator(ctx, args[0].createScalarEvaluator(ctx), sourceLoc) {

@Override
protected FunctionIdentifier getIdentifier() {
return ABooleanDefaultNullConstructorDescriptor.this.getIdentifier();
@@ -30,14 +30,16 @@
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;

@MissingNullInOutFunction
@MissingNullInOutFunction(onMissing = MissingNullInOutFunction.MissingNullType.NULL)
public class ADateDefaultNullConstructorDescriptor extends AbstractScalarFunctionDynamicDescriptor {

private static final long serialVersionUID = 1L;
public static final IFunctionDescriptorFactory FACTORY = ADateDefaultNullConstructorDescriptor::new;

@Override
public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
return new IScalarEvaluatorFactory() {

private static final long serialVersionUID = 1L;

@Override
@@ -31,14 +31,16 @@
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;

@MissingNullInOutFunction
@MissingNullInOutFunction(onMissing = MissingNullInOutFunction.MissingNullType.NULL)
public class ADateDefaultNullConstructorWithFormatDescriptor extends AbstractScalarFunctionDynamicDescriptor {

private static final long serialVersionUID = 1L;
public static final IFunctionDescriptorFactory FACTORY = ADateDefaultNullConstructorWithFormatDescriptor::new;

@Override
public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
return new IScalarEvaluatorFactory() {

private static final long serialVersionUID = 1L;

@Override
@@ -52,10 +54,7 @@ protected FunctionIdentifier getIdentifier() {

@Override
protected boolean checkAndSetMissingOrNull(IPointable result) throws HyracksDataException {
if (PointableHelper.checkAndSetNull(result, inputArg)) {
return true;
}
return super.checkAndSetMissingOrNull(result);
return PointableHelper.checkAndSetNull(result, inputArg, formatArg);
}
};
}

0 comments on commit ded5f56

Please sign in to comment.