Repository: trinodb/trino (Java)
Date: 2019-11-22 07:35:04+00:00
Task type: Bug Fix

Issue:
Using dot in ScalarFunctions name value cause failures
We are wring a custom functions like `@ScalarFunction(value = "pretradedate", alias = "default.pretradedate")`. When select on `default.pretradedate`, it throws error
```
io.prestosql.spi.PrestoException: line 1:1: Function default.pretradedate not registered

	at io.prestosql.sql.analyzer.ExpressionAnalyzer$Visitor.visitFunctionCall(ExpressionAnalyzer.java:878)
	at io.prestosql.sql.analyzer.ExpressionAnalyzer$Visitor.visitFunctionCall(ExpressionAnalyzer.java:316)
	at io.prestosql.sql.tree.FunctionCall.accept(FunctionCall.java:110)
	at io.prestosql.sql.tree.StackableAstVisitor.process(StackableAstVisitor.java:26)
	at io.prestosql.sql.analyzer.ExpressionAnalyzer$Visitor.process(ExpressionAnalyzer.java:339)
	at io.prestosql.sql.analyzer.ExpressionAnalyzer.analyze(ExpressionAnalyzer.java:276)
	at io.prestosql.sql.ExpressionTestUtils.planExpression(ExpressionTestUtils.java:92)
	at io.prestosql.operator.scalar.FunctionAssertions.createExpression(FunctionAssertions.java:641)
	at io.prestosql.operator.scalar.FunctionAssertions.executeProjectionWithAll(FunctionAssertions.java:480)
	at io.prestosql.operator.scalar.FunctionAssertions.selectUniqueValue(FunctionAssertions.java:295)
	at io.prestosql.operator.scalar.FunctionAssertions.selectSingleValue(FunctionAssertions.java:290)
	at io.prestosql.operator.scalar.FunctionAssertions.assertFunction(FunctionAssertions.java:253)
	at io.prestosql.operator.scalar.AbstractTestFunctions.assertFunction(AbstractTestFunctions.java:91)
	at cn.com.gf.bdp.presto.udf.tradingday.TradeDateFunctionTest.testPretradeDate(TradeDateFunctionTest.java:56)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
	at org.testng.TestRunner.privateRun(TestRunner.java:756)
	at org.testng.TestRunner.run(TestRunner.java:610)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
	at org.testng.SuiteRunner.run(SuiteRunner.java:289)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)
	at org.testng.TestNG.runSuites(TestNG.java:1133)
	at org.testng.TestNG.run(TestNG.java:1104)
	at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
	at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Caused by: io.prestosql.spi.PrestoException: Function default.pretradedate not registered
	at io.prestosql.metadata.FunctionRegistry.resolveFunction(FunctionRegistry.java:706)
	at io.prestosql.metadata.MetadataManager.lambda$resolveFunction$19(MetadataManager.java:1312)
	at java.util.Optional.orElseGet(Optional.java:267)
	at io.prestosql.metadata.MetadataManager.resolveFunction(MetadataManager.java:1312)
	at io.prestosql.sql.analyzer.ExpressionAnalyzer$Visitor.visitFunctionCall(ExpressionAnalyzer.java:866)
	... 37 more
```
Presto version: 323


Issue discussion:
@findepi @martint @Praveen2112 

Solution patch:
diff --git a/presto-main/src/main/java/io/prestosql/sql/gen/BytecodeUtils.java b/presto-main/src/main/java/io/prestosql/sql/gen/BytecodeUtils.java
index 95c6fec9db21..b71d6a895ec4 100644
--- a/presto-main/src/main/java/io/prestosql/sql/gen/BytecodeUtils.java
+++ b/presto-main/src/main/java/io/prestosql/sql/gen/BytecodeUtils.java
@@ -335,7 +335,8 @@ else if (type == Boolean.class) {
 
     public static BytecodeExpression invoke(Binding binding, String name)
     {
-        return invokeDynamic(BOOTSTRAP_METHOD, ImmutableList.of(binding.getBindingId()), name, binding.getType());
+        // ensure that name doesn't have a special characters
+        return invokeDynamic(BOOTSTRAP_METHOD, ImmutableList.of(binding.getBindingId()), name.replaceAll("[^(A-Za-z0-9_$)]", "_"), binding.getType());
     }
 
     public static BytecodeExpression invoke(Binding binding, Signature signature)


Test patch:
diff --git a/presto-main/src/test/java/io/prestosql/operator/scalar/CustomFunctions.java b/presto-main/src/test/java/io/prestosql/operator/scalar/CustomFunctions.java
index 63a71b6b589d..9ffb1cee2ad4 100644
--- a/presto-main/src/test/java/io/prestosql/operator/scalar/CustomFunctions.java
+++ b/presto-main/src/test/java/io/prestosql/operator/scalar/CustomFunctions.java
@@ -45,4 +45,11 @@ public static boolean customIsNullBigint(@SqlNullable @SqlType(StandardTypes.BIG
     {
         return value == null;
     }
+
+    @ScalarFunction(value = "identity.function", alias = "identity&function")
+    @SqlType(StandardTypes.BIGINT)
+    public static long customIdentityFunction(@SqlType(StandardTypes.BIGINT) long x)
+    {
+        return x;
+    }
 }
diff --git a/presto-main/src/test/java/io/prestosql/operator/scalar/TestCustomFunctions.java b/presto-main/src/test/java/io/prestosql/operator/scalar/TestCustomFunctions.java
index 7349d740137d..b98c66d32ad7 100644
--- a/presto-main/src/test/java/io/prestosql/operator/scalar/TestCustomFunctions.java
+++ b/presto-main/src/test/java/io/prestosql/operator/scalar/TestCustomFunctions.java
@@ -47,4 +47,11 @@ public void testLongIsNull()
         assertFunction("custom_is_null(CAST(NULL AS BIGINT))", BOOLEAN, true);
         assertFunction("custom_is_null(0)", BOOLEAN, false);
     }
+
+    @Test
+    public void testIdentityFunction()
+    {
+        assertFunction("\"identity&function\"(\"identity.function\"(123))", BIGINT, 123L);
+        assertFunction("\"identity.function\"(\"identity&function\"(123))", BIGINT, 123L);
+    }
 }