Respect null-call clause in SQL function execution
@@ -46,6 +46,9 @@ import static com.facebook.presto.bytecode.instruction.Constant.loadFloat;
import static com.facebook.presto.bytecode.instruction.Constant.loadInt;
import static com.facebook.presto.bytecode.instruction.Constant.loadLong;
import static com.facebook.presto.bytecode.instruction.Constant.loadString;
+import static com.facebook.presto.spi.relation.SpecialFormExpression.Form.IS_NULL;
+import static com.facebook.presto.spi.relation.SpecialFormExpression.Form.OR;
+import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.sql.gen.BytecodeUtils.generateWrite;
import static com.facebook.presto.sql.gen.BytecodeUtils.loadConstant;
import static com.facebook.presto.sql.gen.LambdaBytecodeGenerator.generateLambda;
@@ -103,9 +106,10 @@ public class RowExpressionCompiler
{
FunctionManager functionManager = metadata.getFunctionManager();
FunctionMetadata functionMetadata = functionManager.getFunctionMetadata(call.getFunctionHandle());
+ BytecodeGeneratorContext generatorContext;
switch (functionMetadata.getImplementationType()) {
case BUILTIN:
- BytecodeGeneratorContext generatorContext = new BytecodeGeneratorContext(
+ generatorContext = new BytecodeGeneratorContext(
RowExpressionCompiler.this,
context.getScope(),
callSiteBinder,
@@ -122,11 +126,34 @@ public class RowExpressionCompiler
newCompiledLambdaMap.putAll(generateMethodsForLambda(classDefinition, callSiteBinder, cachedInstanceBinder, function, metadata, sqlFunctionProperties, "sql"));
// generate bytecode for SQL function
- return new RowExpressionCompiler(classDefinition, callSiteBinder, cachedInstanceBinder, fieldReferenceCompiler, metadata, sqlFunctionProperties, newCompiledLambdaMap.build()).compile(
+ RowExpressionCompiler newRowExpressionCompiler = new RowExpressionCompiler(classDefinition, callSiteBinder, cachedInstanceBinder, fieldReferenceCompiler, metadata, sqlFunctionProperties, newCompiledLambdaMap.build());
+ // If called on null input, directly use the generated bytecode
+ if (functionMetadata.isCalledOnNullInput() || call.getArguments().isEmpty()) {
+ return newRowExpressionCompiler.compile(
function,
context.getScope(),
context.getOutputBlockVariable(),
context.getLambdaInterface());
+ }
+
+ // If returns null on null input, generate if(any input is null, null, generated bytecode)
+ generatorContext = new BytecodeGeneratorContext(
+ newRowExpressionCompiler,
+ context.getScope(),
+ callSiteBinder,
+ cachedInstanceBinder,
+ functionManager);
+
+ return (new IfCodeGenerator()).generateExpression(
+ generatorContext,
+ call.getType(),
+ ImmutableList.of(
+ call.getArguments().stream()
+ .map(argument -> new SpecialFormExpression(IS_NULL, BOOLEAN, argument))
+ .reduce((a, b) -> new SpecialFormExpression(OR, BOOLEAN, a, b)).get(),
+ new ConstantExpression(null, call.getType()),
+ function),
+ context.getOutputBlockVariable());
default:
throw new IllegalArgumentException(format("Unsupported function implementation type: %s", functionMetadata.getImplementationType()));
